Back to Nodes

empirio.ai

Last updated Sep 11, 2026

n8n community nodes for empirio.ai: build, publish and analyse surveys, and trigger workflows on every new survey response.

187 Weekly Downloads
687 Monthly Downloads

Included Nodes

empirio.ai
empirio.ai Trigger

Description

@empirio-ai/n8n-nodes-empirio-ai

n8n community nodes for empirio.ai — build, publish and analyse online
surveys, and start a workflow the moment a respondent submits an answer.

The package contains two nodes:

| Node | Type | What it does |
| ———————- | ——- | ————————————————————————————————————————————————————————- |
| empirio.ai | Action | Create, update, publish, duplicate and delete surveys; add, move, change and remove questions, answer options, logic rules and translations; read full draft/published definitions, responses, aggregates, non-matrix cross-tabulations, statistics and exports |
| empirio.ai Trigger | Trigger | Runs the workflow whenever a response is submitted, delivered over a signed webhook |

Installation · Credentials · empirio.ai node ·
empirio.ai Trigger · Use as an AI tool ·
Self-hosted and Cloud · Rate limits ·
Compatibility

Installation

n8n Cloud

Open the nodes panel on the canvas, search for empirio.ai under More from the community, select
the node and click Install. Only instance owners and admins can install verified community nodes.

Self-hosted n8n

Open Settings → Community nodes → Install a community node, enter @empirio-ai/n8n-nodes-empirio-ai and
confirm.

To install manually instead, run this in your n8n data folder (~/.n8n by default):

npm install @empirio-ai/n8n-nodes-empirio-ai

Restart n8n afterwards.

Requirements

  • An empirio.ai account on any plan, including Free. The platform API and webhooks are not
  • gated on a plan. Some individual survey features still are, refusing with
    planfeaturesexceeded and naming the plan each one needs, and the two export
    operations need a paid plan of their own (responses from Lite, charts from Plus),
    refusing with planfeaturerequired.

  • n8n 2.x. Development and the test suite run against 2.35.7.
  • Credentials

    Both nodes offer API key, automatic OAuth2 and own-client OAuth2 credentials. Pick
    whichever suits the deployment — an API key is the fastest to set up, OAuth2 avoids storing a
    long-lived key.

    empirio.ai API (API key)

    1. Sign in at empirio.ai and open Integrations.
    2. Under REST API, create a key. The key is shown once — copy it right away.
    3. In n8n, create an empirio.ai API credential and paste the key into API Key.

    OAuth2 (Automatic)

    For OAuth2 (Automatic), create an empirio.ai OAuth2 credential and click Connect my account.
    n8n registers a public client through Dynamic Client Registration and uses PKCE S256.
    Approve surveys, responses and webhooks together. This connection serves both nodes and refreshes
    access tokens automatically. It works on n8n versions with OAuth2 Dynamic Client Registration,
    including Cloud and self-hosted instances. Client ID and secret entry is unnecessary.

    OAuth2 (Own Client)

    For OAuth2 (Own Client), register a confidential client with the redirect URL of your n8n host.

    1. In n8n, create an empirio.ai OAuth2 API credential. n8n shows an OAuth Redirect URL at the
    top of the credential — copy it. It looks like
    https:///rest/oauth2-credential/callback.
    2. Sign in at empirio.ai, open Integrations and go to **OAuth
    clients**.
    3. Create a client and paste the copied redirect URL into Redirect URIs. empirio.ai shows the
    Client ID and Client secret once — copy both.
    4. Back in n8n, paste them into Client ID and Client Secret, then click **Connect my
    account** and approve the consent screen.

    The credential uses the authorization code flow with PKCE (S256) and requests the surveys,
    responses and webhooks scopes. Access tokens are refreshed automatically.

    webhooks is required by the trigger node, which registers a delivery endpoint on the survey when
    a workflow is activated and removes it on deactivation. If trigger activation reports an
    insufficient scope, reconnect the credential and approve the scopes shown above.

    > The redirect URL must match exactly, including the scheme and any port. If your n8n host name
    > changes, register the new redirect URL with the OAuth client as well.

    Testing a credential

    All credential types have a built-in test that calls GET /me. Click Save and n8n reports
    whether the connection works. It reports nothing else — n8n shows the outcome of a credential test
    as a pass/fail message and does not surface the response body, so the plan and the granted scopes
    that /me returns are not displayed. A passing test therefore confirms the credential and the host,
    not the scopes — a grant missing webhooks still passes here and fails when the trigger registers.

    Neither credential has a host or environment field: baseUrl is a hidden, constant property fixed
    at https://platform.empirio.ai/rest/v1.

    empirio.ai node

    Choose a Resource, then an Operation. Surveys are picked from a searchable dropdown, or
    addressed by survey URL or ID.

    Survey

    | Operation | Description |
    | ———— | —————————————————————————————————————————————————————————————————————————– |
    | Create | Create a draft, either from a prompt (AI mode) or from explicit questions (Manual mode) |
    | Delete | Delete a survey and its responses; takes effect immediately and cannot be undone |
    | Duplicate | Copy a survey into a new unpublished draft |
    | Get | Read the complete working draft and latest published definition, including plan_gate.status (clear, blocked, or unresolved) and blocking feature details |
    | Get Many | List surveys owned by or shared with the account |
    | Get Stats | View count, started and completed responses, completion rate and completion time |
    | Publish | Publish the working draft and return the public survey URL; a blocked owner-plan gate returns planfeaturesexceeded |
    | Revert Draft | Throw the working draft away and put the published version back in it. Every unpublished change goes, and nothing you can call brings it back. See Use as an AI tool before attaching this to an agent. |
    | Unpublish | Take a survey offline and move it back to draft |
    | Update | Apply changes to the working draft without publishing |

    Survey → Update offers Input → Builder / JSON in Manual mode. Builder is the default. It has
    four repeatable groups: Questions (add, update, delete, reorder), Answer Options
    (add, rename, delete, reorder), Logic Rules (add, update, delete), and Translations (survey text,
    question text, answer labels). Each row shows the fields for its selected change. Adding questions
    reuses Create’s type-specific fields. Multiple groups and general settings become one PATCH.

    Question and answer reordering takes the complete ordered list of current IDs. Option changes and
    label translations support answer options, matrix rows and matrix columns. Dropdowns read the
    working draft and resolve the question and list from the current row. Translation rows can cover
    multiple questions and languages; conflicting values for the same translation are rejected.

    An update writes only the fields explicitly added to its collection. false is a value, and
    Remove Subtitle explicitly clears and hides the subtitle. JSON mode accepts the four API sections
    for advanced edits. Hidden fields from the other input mode are ignored, so a workflow that sends its
    changes as JSON has to set Input to JSON.

    Answer options

    Read answer options through Survey → Get, inside each question in draft.questions.
    Survey → Update adds, renames, removes and reorders them. Rename preserves option IDs and
    attached responses, rules and translations. The node has only the Survey and Response resources;
    dropdowns inside the Update builder resolve option IDs from the selected question.

    The language dropdown lists the survey’s additional languages. Translate the survey text, questions
    and answer labels to provide a complete language. Inspect warnings: text for an unconfigured
    language is ignored by the API. The result retains applied_changes and warnings.

    Get returns the REST payload unchanged. draft is the complete working definition and
    published is the latest published definition or null before the first publish. Analytics
    question dropdowns read published.questions, so draft-only questions are never offered for
    response filters. Translation entries have to be read together with their status: pending
    content may be master-locale text copied in as an editable placeholder, not a completed
    target-locale translation. Get Stats remains on the Survey resource because it describes the survey as a
    whole even though its REST endpoint sits under /responses/.

    Create and Update both accept an AI mode, where empirio.ai generates the change from a prompt,
    and a Manual mode with Builder and JSON inputs. JSON accepts question definitions on Create and
    question, option, logic-rule operations and translations on Update. General settings remain field
    collections in both input modes. Logic Rule Operations accepts add, update and delete entries;
    the logic_rules list returned by Get is the stored state, not an edit script.

    All twenty-one question types are available, the two image ones included. An option of an
    Image Single Choice or Image Multiple Choice question carries an Image URL: give an
    https address and empirio.ai fetches the picture, checks it is a PNG, JPEG or WebP within 5 MB,
    6000 px per side and 16 megapixels, and stores it. The storage path Get reports is accepted
    there too, which is what lets a survey be read and written back unchanged, and a URL that cannot
    be fetched refuses the whole call rather than creating a picture round with a gap in it. The same
    field sets the survey logo, under Additional Fields → Logo URL on Create.

    Both Create and Update carry Run Start and Run End — the survey’s run window. It
    activates and deactivates itself at those instants without anybody publishing again, and unlike
    every other field it takes effect immediately. Leave one empty to keep what is stored; send the
    literal null to remove it.

    Create reaches every settings key the API takes — Additional Locales, Hide Branding,
    Master Locale and Multi Language Enabled alongside the three toggles.

    Logic Rules and Translations appear only when Questions is set to JSON. Both name the
    questions and the answer options of the same request by the ref given alongside them, and the
    Questions builder offers no refs — a form built for people who do not write JSON should not ask
    them to invent identifiers.

    With the builder, logic and translations are a second node: Create reports every identifier it
    assigned under created — each question and each answer option — and Update takes those ids
    directly. In JSON mode the whole survey, its logic and a second language fit in one node, and the
    name is stored: Get returns it, and every later call may write it wherever a question_id or an
    option_id is expected, including an option added by that same Update. Update additionally takes Logo Path, the storage asset path Get returns
    under design — the literal null removes the logo. There is no logo on Create: the path
    has to contain the survey ID, which the create is what mints.

    Every one of those sections has to carry something. An empty [] or {} is the right shape and
    still asks for no change, and the API refuses an update that requests nothing — so the node refuses
    it first, naming the field you filled in. **Leave a section out entirely to mean “do not touch this
    one.”** The two are not the same thing and the node keeps them apart: a field you never filled in is
    omitted in silence, while an expression that resolved to no entries fails the item, because that is
    usually a workflow that meant to send something and found nothing. Gate the update on an If node
    when a section may legitimately turn out empty.

    Revert Draft is the way back out of an Update that went wrong, and it is not an undo of the last
    one: it replaces the whole working draft with whatever is currently published, so every
    unpublished change is discarded at once, and no call you can make brings it back. What it
    leaves alone is the published survey — same public link, same responses, still live — which is also
    why a revert leaves no visible mark: nothing on any read surface shows that the work existed. The
    item the operation emits is the only account of it you get, under discarded_changes: which sections
    differed, the question count before and after, and the wording of every question the draft had
    added, removed or changed. The two counts are snapshot sizes rather than a change indicator, so
    equal counts can still accompany additions, removals or modifications. Keep that item if the
    questions may have to be re-entered.

    empirio.ai does keep a short revision history of working drafts on the server side, but it is not an
    undo you can build a workflow on: it is unreachable from the API, and it captures at most one
    revision per survey every 30 seconds, so an Update followed straight by a Revert Draft usually
    records nothing. It is something support may be able to look at after the fact, and nothing more.

    Two answers are worth knowing before you build on it. A survey that has never been published has
    nothing to fall back on and is refused with a 400 naming both ways on — publish the draft first, or
    delete the survey; the draft is not silently emptied. And a draft that already matches the published
    version is a success, not a failure: the item comes back with discarded: false and an empty
    summary, so a workflow that reverts unconditionally does not need to branch on whether there was
    anything to revert.

    Response

    | Operation | Description |
    | —————- | ——————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————— |
    | Delete | Delete responses named by response_id, by row number, or every response of a survey once Confirm Delete All is on. The responses leave every read surface immediately and no API call brings them back. See Use as an AI tool before attaching this to an agent. |
    | Export Charts | Render the results as charts: PowerPoint, PowerPoint with raster slides, PDF, DOCX, ZIP of images, or chart JSON. Every format answers with the finished file in one call — the four image-based ones are rendered in the background and the request waits for them. Chart JSON keeps complete configured option and bounded-scale axes, including unused values with count: 0. |
    | Export Responses | Export the raw participation data as CSV, XLSX, JSON or SPSS. |
    | Get Aggregates | Sparse per-question answer buckets that omit zero-count values, with respondent-based percentages and row/column counts for matrix questions |
    | Get Crosstab | Cross-tabulate the answers of two questions |
    | Get Many | List participations with their answers |

    Both exports take Answer Filters, which narrows the export to the responses that answered
    particular questions particular ways — the same filters the results view offers, and the only way
    to export a segment. Both operations expose their file type through Format, matching the REST
    API, MCP and Make field name format.

    Get Many pages through the API automatically when Return All is on. Its Since option takes
    an ISO timestamp and returns only responses submitted after it, which makes a scheduled polling
    workflow straightforward when a webhook is not an option.

    Two of its options cannot be combined: Order set to Ascending together with **Include
    Incomplete Participations**. The node refuses that pair itself and sends no request at all, so the
    message names the two fields you filled in rather than arriving as an API error. The reason is not
    sorting — Descending with Include Incomplete Participations works fine: an ascending page is a
    descending window mirrored against the total number of responses, and that total counts only
    complete participations, so including the incomplete ones would make the mirrored window slide off
    the end and drop the oldest rows. Leave the order at its descending default, or exclude incomplete
    participations; row_no is the newest-first rank under either order, so a descending page addresses
    exactly the same responses.

    Delete offers three ways to say which responses to remove, and the choice matters. **Response
    IDs** is where a new node starts. It names the responses by the response_id a read surface
    returned — the trigger emits one on every delivery, and every row of Get Many carries one — and an
    ID means the same participation under every filter and at every moment. Selected Rows names them
    by rowno instead, and is a deliberate switch away from that: a rowno is a rank over the
    responses that exist at the moment of the delete, not a handle on a response, so a participation
    submitted between your Get Many and your delete shifts every rank by one and row_numbers: [1]
    then removes the arrival you never saw. Two deletes in one workflow both saying “row 1” remove two
    different responses for the same reason.

    A rank is also a rank inside a filtered set, which is the half that gets data destroyed. Get Many narrows its list with Date From, Date To and Include Incomplete Participations,
    and row_no counts within whatever those leave — so “row 1” of a filtered list and “row 1” of the
    survey can be two different participations. Selected Rows therefore offers the same three under
    Filters, and they have to be set to the same values the Get Many that produced the numbers
    used. Set them on one side only and the delete still answers 200 deleted_count: 1 while removing a
    response that was never in your list. Nothing in the node can catch that: from inside it, a filtered
    Get Many feeding an unfiltered delete looks exactly like an unfiltered one feeding an unfiltered
    delete, which is correct. That is why the mode is an opt-in rather than a warning. Prefer
    Response IDs unless all three filters are deliberately repeated.

    All three can change what a rank names, and none of them is the safe one to leave off:

    | filter | what it does to the ranks | a mismatch |
    | ————————————- | ——————————————————————————————————————————————- | ——————————————————– |
    | Date To | trims the newest end, so every row moves up | picks a different participation, reported as success |
    | Date From | trims the oldest end; incomplete participations rank after every response, so shortening the response section moves each of them up | picks a different participation, reported as success |
    | Include Incomplete Participations | appends the started-but-unsubmitted participations after every response, adding ranks that a delete resolves to submitted responses instead | picks a different participation, reported as success |
    | Order | nothing — row_no is newest-first either way, and there is no Order filter on the delete | harmless |

    Date From looks like the exception and is not, which is worth one worked example. On a survey of
    six responses and two incomplete participations, with Include Incomplete Participations on,
    adding a Date From moved the two incomplete rows from rows 7 and 8 to rows 3 and 4 — the
    responses kept their numbers, the appended rows did not. A delete that repeated the **Include
    Incomplete Participations and left the Date From** off then answered
    200 {"deletedcount": 1, "unresolvedrow_numbers": []}, destroying the response that was row 3 of
    the unfiltered list — one the workflow had never listed — while the row it had actually selected
    survived. Repeat all three, or use Response IDs.

    Incomplete participations are also the one kind of row a row_no cannot address: a rank always
    resolves to a submitted response, so a part_… row you want gone has to be named in **Response
    IDs** (400 by rank, 200 by ID). That is a second reason not to read a rank off a list you
    produced with Include Incomplete Participations on.

    Nothing checks that for you. The two selectors live on two nodes, and no n8n node can read another
    node’s parameters, so the node states the requirement in the editor and cannot enforce it. That is
    the practical reason to prefer Response IDs whenever the numbers come from a previous node:
    Get Many returns a responseid on the same row as the rowno, and an ID means the same
    participation under every filter and at every moment.

    Response IDs has neither problem, and it is also the only selector that is safe to retry. An ID
    that names nothing in this survey comes back under unresolvedresponseids and the rest are still
    deleted; an ID whose response was already deleted comes back under alreadydeletedresponse_ids,
    and when that is true of every ID you named the call answers deleted_count: 0 — so re-running a
    delete that already succeeded reports what happened instead of deleting something else. Both fields arrive on the item next to deleted_count. Use row numbers
    only for a one-off clean-up you are watching; use IDs for anything a workflow runs on its own.

    All Responses takes no filters at all. It has no selection to narrow, and the API refuses every
    narrowing field on that mode by name — including Include Incomplete Participations switched
    off, because n8n sends a filter you added even at its own default. So the Filters control is
    shown for Selected Rows only; switch to All Responses and it disappears, and a value left
    behind in it from an earlier edit is not sent.
    Get Aggregates likewise reports unresolvedquestionids next to its counts — question IDs that
    match nothing in the published survey. Check it before reading an absent question as “no answers”:
    a mistyped ID, or one that exists only in the working draft, lands there rather than in the results.
    Its buckets are sparse and omit configured values with no responses. Percentages use respondents
    who answered the question as their denominator, so a multi-select question can sum above 100%; its
    totalSelections field reports the number of selections instead.

    The question dropdowns of Get Aggregates and Get Crosstab are filled from the survey you
    selected, so you never have to look up a question ID by hand. Questions that cannot answer the
    operation are left out: display-only content blocks everywhere, because they hold no answers, and
    matrix questions in Get Crosstab, which cannot cross-tabulate them.

    Get Aggregates reports each bucket by its label, not by an option ID. Two values escape that
    resolution and arrive raw: a free-text “other” answer, which is the text the respondent typed, and
    an option deleted after the response was submitted, which has no label left and stays an opt_…
    id. Current option IDs and labels come from Survey → Get under published.questions; an ID absent
    there identifies historical data whose current published definition no longer has that option.

    Chart exports answer with the finished file in one call, in every format. The four image-based
    formats — DOCX, PDF, PowerPoint (Images) and ZIP of Images — render in a background
    job on the API side, but the request waits for it, so the node sees the same shape as for
    PowerPoint (Editable) and Chart JSON. A render takes seconds on a typical survey and about a
    minute on the largest ones, well inside n8n’s 300-second request timeout.

    Chart JSON intentionally uses a denser representation than Get Aggregates for question types with
    configured options or a bounded scale: the complete configured axis is present, including unused
    values with count: 0, and historical answered values remain visible after the configured domain
    changes. Its totalAnswers counts plotted bucket mentions, which means selections rather than
    respondents for multi-select questions.

    empirio.ai Trigger

    The trigger registers a webhook on the selected survey when the workflow is activated, and removes
    it again when the workflow is deactivated. Every delivery is signed with a per-webhook secret, and
    the trigger verifies the X-EmpirioAi-Signature HMAC before it emits anything. A delivery with a
    missing or wrong signature is answered with HTTP 401 and never starts the workflow. empirio.ai
    treats any 2xx as delivered, so answering 401 is what makes it retry and then surface a failed
    delivery — a silent 200 would discard the response and leave both sides reporting success.

    Event is Response Submitted, the one event empirio.ai delivers.

    Output controls the shape of the emitted item:

  • Flat Answers (default) — one item per response with the answers keyed by question title,
  • next to the response metadata (responseid, respondedat, surveyid, surveyversion,
    duration_seconds, …). This is what most workflows want: the keys read like the survey, so you
    can map them straight into a spreadsheet row, a CRM record or a database column set. An
    allow_other question contributes a second key, (Other)</code>. Two questions sharing a title,<br /> or a title that collides with one of the metadata keys, are disambiguated with a <code> (2)</code>, <code> (3)</code> …<br /> suffix, and the companion key follows the key its question actually got — the second <em>Comments?</em><br /> arrives as <code>Comments? (2)</code> and <code>Comments? (2) (Other)</code>. Three kinds of question have no title to<br /> key by and fall back to their raw <code>question<em>id</code>, which is where a <code>q</em>…</code> key can still turn up: one<br /> that was deleted from the survey after this response was stored, one whose title is empty, and one<br /> whose title is <code>null</code>.</p> <li><strong>Full Payload</strong> — the complete delivery, including the per-cell <code>answers</code> array that mirrors the</li> <p> CSV export layout (one entry per scalar answer, multiple-choice option, matrix row, rank position<br /> and free-text companion). Every entry carries <code>question<em>id</code>, <code>question</em>title</code> and <code>question_type</code> —<br /> so a <code>Switch</code> node can branch on the type without looking anything up, and a report can print the<br /> title without a second call. Beyond those three an entry carries only the identifiers its own kind<br /> has. A multiple-choice entry has <code>option<em>id</code> next to <code>option</em>label</code>, a matrix row has <code>row_id</code> and<br /> <code>selected<em>column</em>id</code>, a single-select or rank entry has <code>selected<em>option</em>id</code> — and a text, number<br /> or date entry has <strong>none of them</strong>: it is <code>question<em>id</code> + <code>value</code>, with <code>selected</em>option_id</code><br /> absent rather than <code>null</code>. Entries are told apart by which of those identifier fields is present,<br /> not by a discriminator field.</p> <p>Pick by what consumes the item. <strong>Flat Answers</strong> is readable but keyed on text a survey editor can<br /> change: rename a question and its key changes with it, and a workflow mapped to the old key stops<br /> finding its value. <strong>Full Payload</strong> keys on identifiers that a rename does not touch, which is what<br /> you want once something downstream depends on the mapping staying put — with the caveat that a<br /> matrix or ranking entry reports the identifier as it was stored, so it can still name an option the<br /> survey no longer defines.</p> <p>> <strong>A key that disappears fails silently.</strong> n8n’s expression evaluator answers a missing key with<br /> > <code>null</code> instead of raising — so a node reading <code>{{ $json['Old title'] }}</code> after a rename stays<br /> > <strong>green</strong> and writes an empty value downstream. It does not error, it does not warn, and typing<br /> > the field as a number or boolean does not catch it either. Nothing but the data will tell you.<br /> > If that matters, use <strong>Full Payload</strong> or add an <code>If</code> node that asserts the key is present.</p> <p><strong>Options → Include Unanswered Questions</strong> (Flat Answers only) emits <code>null</code> for questions the<br /> respondent skipped or logic hid. Without it an item only carries the questions that were actually<br /> answered, so the key set differs from response to response and the columns of a downstream<br /> spreadsheet or database node shift. Turn it on when something downstream expects a fixed shape.<br /> Display-only <code>content</code> blocks stay absent either way — there is no answer to report a <code>null</code> for.</p> <p><strong>Options → Label</strong> sets the name shown for the webhook in the empirio.ai dashboard. It defaults to the<br /> workflow name. It applies when the trigger registers a <strong>new</strong> webhook; a registration the trigger<br /> adopts (see below) keeps the name and owner it already had.</p> <p>Question IDs are stable for the lifetime of a survey. Inserting, removing or reordering questions<br /> never shifts an existing ID, so a mapping built on <strong>Full Payload</strong> keeps working, and the same ID<br /> also identifies the question in the empirio.ai node’s analytics operations.</p> <h3>Recovering a lost registration</h3> <p>If a workflow is imported, duplicated or restored, n8n may no longer hold the webhook state. On the<br /> next activation the trigger looks up the survey’s webhooks, matches its own callback URL and adopts<br /> the existing registration. Because empirio.ai hands out a webhook’s signing secret only once, the<br /> trigger rotates the secret in that case so it can verify deliveries again. Rotating affects only<br /> this registration — other webhooks on the survey are untouched.</p> <h2>Use as an AI tool</h2> <p>The empirio.ai node is <code>usableAsTool</code>, so it can be attached to an <strong>AI Agent</strong> or exposed through an<br /> <strong>MCP Server Trigger</strong>. Read this before you attach one that can write.</p> <p><strong>What the model can drive.</strong> n8n publishes a tool schema built from the node’s parameters. It builds<br /> it by walking the <em>stored</em> parameter values and collecting every <code>$fromAI(...)</code> call it finds, whatever<br /> the field is — so each parameter the workflow author filled in with such an expression becomes an<br /> input the model supplies. Anything the author typed in as a literal is fixed and the model cannot<br /> change it. A <code>tools/call</code> supplying <code>confirmDeleteAll: true</code> performs a real delete of every response<br /> in the survey; one supplying <code>deleteMode: "ids"</code> and a list of <code>responseIds</code> deletes exactly the<br /> participations it names.</p> <p><strong>Response → Delete</strong> has <strong>nine</strong> such inputs, not the four or five you might count off the fields<br /> in front of you: <code>deleteMode</code>, <code>responseIds</code>, <code>rowNumbers</code>, <code>confirmDeleteAll</code>, the three <strong>Filters</strong><br /> entries <code>dateFrom</code> / <code>dateTo</code> / <code>includeIncompleteParticipations</code>, <code>idempotencyKey</code>, and the<br /> <strong>Survey</strong> field. Nine is the ceiling, and reaching it takes a node whose <code>Delete Mode</code> is itself<br /> model-filled. The walk runs over the parameters <em>as the mode displays them</em>, so pinning <code>Delete Mode</code><br /> to a literal removes the <strong>other</strong> modes’ fields from the published schema — measured on five nodes of<br /> one MCP Server Trigger whose stored parameters were identical except for that one value:</p> <p>| stored <code>Delete Mode</code> | published tool inputs |<br /> | —————————————- | ——————————————————————————————————- |<br /> | <code>$fromAI(…)</code> | 9 — every field above |<br /> | <code>"ids"</code> | 3 — <code>surveyId</code>, <code>responseIds</code>, <code>idempotencyKey</code> |<br /> | <code>"rows"</code> | 6 — <code>surveyId</code>, <code>rowNumbers</code>, <code>dateFrom</code>, <code>dateTo</code>, <code>includeIncompleteParticipations</code>, <code>idempotencyKey</code> |<br /> | <code>"all"</code> | 3 — <code>surveyId</code>, <strong><code>confirmDeleteAll</code></strong>, <code>idempotencyKey</code> |<br /> | <em>not set</em> — the node left on its default | 3 — the <code>"ids"</code> row: <code>surveyId</code>, <code>responseIds</code>, <code>idempotencyKey</code> |</p> <p>Pinning the mode is the first item in the list below for a second reason, then: it is the one edit<br /> that shrinks what the model is offered at all. Note what it does <strong>not</strong> do. <code>confirmDeleteAll</code> is<br /> displayed by <code>deleteMode: "all"</code>, so pinning the mode to <code>"all"</code> keeps that field in the schema —<br /> as a <em>required</em> input, measured driving <code>{"confirmDeleteAll": true}</code> to <code>deleted_count: 8</code> on a<br /> mode-pinned tool. Pinning the mode narrows which fields a model can reach; only pinning<br /> <code>Confirm Delete All</code> itself takes the delete-everything switch out of its hands, which is why the<br /> list below names both.</p> <p><code>Confirm Delete All</code> and <code>Delete Mode</code> carry <code>noDataExpression</code>, which removes the expression editor<br /> and the editor’s “let the model define this parameter” button from those two fields. Six visible fields of<br /> this operation do not carry it — <code>Survey</code>, <code>Response IDs</code>, <code>Row Numbers</code> and the three <strong>Filters</strong><br /> entries — so the editor offers the button on those six. <code>idempotencyKey</code> is hidden; a stored<br /> <code>$fromAI</code> expression on it still participates in the measured schema above. And on the two that do<br /> carry it, it is a guard-rail in the editor, <strong>not</strong> an access control: <code>noDataExpression</code> is a<br /> property of the field <em>definition</em>, while the schema is built from the stored <em>values</em>, so a workflow<br /> whose stored parameters already hold <code>$fromAI</code> expressions — hand-written, imported, or built by the<br /> API — publishes them to the model regardless. <code>displayOptions</code> is the only thing that keeps a field<br /> out, and only while the parameter it depends on holds a literal.</p> <p>So the safe shape for an agent-facing delete is:</p> <li>Pin <strong>Delete Mode</strong> and <strong>Confirm Delete All</strong> to literal values in the node. Leaving **Delete</li> <p> Mode** alone gets you the <code>ids</code> schema, which is the right one; pinning it says so explicitly and<br /> survives a future change of default.</p> <li>If the model must choose <em>what</em> to delete, let it fill <strong>Response IDs</strong>, not <strong>Row Numbers</strong>. An ID</li> <p> names one participation for its whole life, so the model deletes what it named and nothing else,<br /> and a retry repeats that same delete. A row number is a rank resolved when the call runs, inside<br /> whatever the <strong>Filters</strong> select: a response arriving in between shifts every rank, and a filter the<br /> model chooses differently from the list it read renumbers the whole set — the model’s “row 1” is<br /> then somebody else, reported as a success. Pinning the mode to <code>ids</code> also takes <code>rowNumbers</code> and<br /> all three filters out of the schema, so there is nothing left for the model to get wrong.</p> <li>Leave <strong>On Error</strong> at its default on a node attached as a tool. The default reports a refusal to</li> <p> the model as <code>isError: true</code> with the API’s own sentence; <code>Continue (using regular output)</code> hands<br /> it <code>[{}]</code>, which is indistinguishable from a successful delete. See<br /> <a href="#errors-reaching-a-model">Errors reaching a model</a>.</p> <li>Pin the <strong>Survey</strong> field. This is the one control that bounds the damage — see below — so an agent</li> <p> should get a node scoped to one survey rather than one whose survey ID it chooses.</p> <li>If an agent needs no destructive power at all, do not attach a node with <code>Delete</code> selected. The</li> <p> operation is part of the tool the model sees, not something it asks permission for. The same goes<br /> for <code>Survey → Delete</code> and <code>Survey → Revert Draft</code> — see<br /> <a href="#survey--revert-draft-and-the-other-whole-survey-writes">Survey → Revert Draft</a>.</p> <p><strong>What the node refuses regardless.</strong> Every delete guard runs on every call, whoever made it. A<br /> <code>mode: "all"</code> request is refused unless <code>confirmDeleteAll</code> is a real <code>true</code>; a value that merely<br /> looks set — <code>"false"</code>, <code>"no"</code>, <code>"0"</code>, a stringified expression — is refused by name rather than read<br /> for truthiness. A <code>Delete Mode</code> that is none of <code>ids</code>, <code>rows</code> and <code>all</code> is refused rather than<br /> falling through all three guards. Each guard is a no-op outside its own mode, so row numbers left in<br /> the node are not sent with an <code>ids</code> delete and vice versa. Every value in <strong>Response IDs</strong> must look<br /> like a <code>response<em>id</code> — a <code>resp</em></code> or <code>part_</code> prefix and 22 more characters — so a row number, a survey<br /> UUID or a truncated ID fails in the node, by name, before anything is sent; an empty list and a list<br /> longer than 1000 are refused the same way. The same discipline applies to every other guarded<br /> parameter in the node.</p> <p><strong>What that does and does not bound.</strong> With <strong>Response IDs</strong> model-fillable, three things limit the<br /> damage. The shape check above means the model cannot turn a number it saw somewhere into a delete.<br /> The API resolves IDs <strong>within the selected survey only</strong>: an ID belonging to a different survey is<br /> reported back under <code>unresolved<em>response</em>ids</code> and deletes nothing there, and a call in which no ID<br /> names a participation of this survey at all — live or already deleted — is refused outright with<br /> <em>“No participation of this survey matches”</em> — so a pinned <strong>Survey</strong> field is a hard ceiling on what<br /> any tool call can reach. And an ID is a 128-bit digest of a random<br /> internal key, so it cannot be guessed or enumerated: the only IDs a model has are the ones a read<br /> surface handed it.</p> <p>What none of that bounds is the survey itself. There is no confirmation toggle on <code>ids</code> the way<br /> <code>confirmDeleteAll</code> gates <code>all</code>, and an agent that has just run <code>Response → Get Many</code> is holding every<br /> <code>response_id</code> in that survey. Within one survey, a model that can call this tool can delete any<br /> participation it chooses, in batches of up to 1000, with no undo. <code>ids</code> is the <em>safer</em> selector —<br /> it deletes only what it names and it is safe to retry — but it is not the <em>weaker</em> one. Treat the<br /> survey pin, and the decision to attach <code>Delete</code> at all, as the access control; the guards above are<br /> only there to stop a malformed call.</p> <h3>Survey → Revert Draft, and the other whole-survey writes</h3> <p><code>Response → Delete</code> is the operation with the most model-reachable surface. It is not the only one<br /> that destroys work, and the operations that do are the ones with the <em>smallest</em> surface — which is<br /> why they are easy to miss on a node built for something else.</p> <p>| operation | published tool inputs | what a call costs |<br /> | ————————- | ——————————– | ————————————————————————— |<br /> | <strong>Survey → Revert Draft</strong> | 2 — <code>surveyId</code>, <code>idempotencyKey</code> | every unpublished change in the draft, unrecoverable; live survey untouched |<br /> | <strong>Survey → Delete</strong> | 2 — <code>surveyId</code>, <code>idempotencyKey</code> | the survey and all its responses |<br /> | Survey → Publish | 2 — <code>surveyId</code>, <code>idempotencyKey</code> | pushes the current draft live |<br /> | Survey → Unpublish | 2 — <code>surveyId</code>, <code>idempotencyKey</code> | takes the survey offline |</p> <p>Two is the floor for a write in this node, and for <strong>Revert Draft</strong> it is also the ceiling: the<br /> operation has no selector, no filters and no confirmation toggle, because there is nothing to select<br /> — it always takes the whole draft of the one survey the node addresses. So on a node whose<br /> <strong>Operation</strong> holds a literal, the only thing a model chooses is <em>which survey</em>, and **pinning the<br /> Survey field is the whole of the remaining access control**, exactly as it is for <code>Delete</code>. An agent<br /> holding an unpinned Revert Draft can empty the draft of every survey in the account, one call each,<br /> and every one of those calls answers <code>200</code>.</p> <p>“Whose <strong>Operation</strong> holds a literal” is a real condition, not a formality. Every number in the table<br /> is measured with <strong>Resource</strong> and <strong>Operation</strong> pinned, which is what a node built in the editor<br /> stores — and the schema is built from stored <em>values</em>, the same door the paragraph above the delete<br /> list describes. A node whose stored <strong>Operation</strong> holds a <code>$fromAI</code> expression publishes <code>operation</code><br /> as a model input as well, next to <code>surveyId</code> and <code>idempotencyKey</code>. Leaving selectors unpinned exposes<br /> the fields of the operations they can select, including the survey builder. On such a node a model<br /> given the tool for <code>Survey → Get</code><br /> can select <code>revertDraft</code> and name the survey in the same call, and the operation the author chose<br /> bounds nothing. <code>Operation</code> and <code>Resource</code> both carry <code>noDataExpression</code>, so the editor never offers<br /> the “let the model define this parameter” button on them — but a workflow imported or built through<br /> the API does not go through the editor. Pin <strong>Operation</strong> as well as <strong>Survey</strong>, and prefer attaching<br /> a node whose selected operation is one you would let the model run unattended.</p> <p>A confirmation toggle would not change that. <code>confirmDeleteAll</code> earns its place on <code>Response →<br /> Delete</code> because that operation <em>has</em> a selector: a model-filled <code>Delete Mode</code> can escalate a named<br /> list of IDs into “every response in the survey”, and the toggle is the second lock on the escalation.<br /> Revert Draft has no such step to lock, so a toggle would only make every legitimate use refuse until<br /> someone switched it on — and it would not be an access control in any case, for the reason the<br /> paragraph above the delete list already gives: the schema is built from <em>stored values</em>, so<br /> <code>noDataExpression</code> keeps a field out of the editor’s expression menu and not out of the model’s<br /> schema.</p> <p>Nothing on a read surface shows that a revert happened. The published survey, its public link and its<br /> responses are exactly as they were, and <code>has<em>pending</em>draft_changes</code> simply reads <code>false</code> — which is<br /> also what an untouched survey reads. The emitted item’s <code>discarded_changes</code> is the only account you<br /> get, so<br /> leave <strong>On Error</strong> at its default (see <a href="#errors-reaching-a-model">Errors reaching a model</a>) and keep<br /> the item if the questions may have to be re-entered.</p> <p><strong>Retries.</strong> See <a href="#idempotency">Idempotency</a>: under an AI Agent, the derived key comes from the<br /> call’s parameters, and under an MCP Server Trigger the node cannot derive a retry-safe key at all.<br /> Revert Draft is idempotent at the API as well — a second call finds the draft already equal to the<br /> published version and answers <code>200</code> with <code>discarded: false</code>, having written nothing.</p> <h3>Errors reaching a model</h3> <p>An API refusal is legible on the two paths you get by default, and illegible on the one you have to<br /> choose. The same refusal, three settings:</p> <p>| setting | what arrives |<br /> | ———————————————— | ———————————————————————————————– |<br /> | default (fail the item) | <code>NodeApiError</code> whose <code>description</code> is the API’s own sentence |<br /> | tool call, default error handling | <code>isError: true</code>, text ending <code>Details: <the API's sentence></code> |<br /> | <strong>On Error</strong> = <code>Continue (using regular output)</code> | workflow item <code>{"error": "Bad request - please check your parameters"}</code>; <strong>tool result <code>[{}]</code></strong> |</p> <p>Both losses are n8n’s, not this node’s, and neither has a hook the node could use. A declarative node<br /> never sees the failure: n8n’s <code>RoutingNode</code> catches it and pushes <code>{ json: {}, error }</code> — the<br /> explanation lives in the sibling <code>error</code>, not in <code>json</code>. The workflow engine then rewrites <code>json</code> from<br /> <code>error.message</code>, the generic HTTP summary, dropping <code>error.description</code>, which is where the API’s<br /> sentence is. The tool wrapper never runs that rewrite at all: it maps a result with<br /> <code>items.flatMap(item => item.json)</code> and <code>JSON.stringify</code>s it, so the sibling is discarded and an empty<br /> object is all that is left. It carries no <code>isError</code>, so a model cannot tell it from a successful write.</p> <p>The one thing to do about it is the setting itself: **leave On Error at its default on any node a<br /> model can call.** If a workflow must continue past a failed delete anyway, note that <code>$json.error</code><br /> downstream holds only that generic summary — the API’s sentence survives in the run’s error record,<br /> which is what the editor shows on the node, not in anything a later node can branch on.</p> <h2>Self-hosted and Cloud</h2> <p>empirio.ai only delivers to endpoints it can reach:</p> <li>The endpoint must be <strong>HTTPS</strong>. Plain <code>http://</code> URLs are rejected.</li> <li>Endpoints resolving to <strong>private or loopback addresses are rejected</strong>, so <code>localhost</code> and</li> <p> <code>192.168.x.x</code> do not work.</p> <p><strong>n8n Cloud</strong> satisfies both conditions out of the box — the empirio.ai Trigger works with no extra<br /> setup.</p> <p><strong>Self-hosted n8n</strong> needs a publicly reachable HTTPS host name. Set <code>WEBHOOK_URL</code> to that public URL<br /> so n8n hands the correct address to empirio.ai, and put a TLS-terminating reverse proxy or tunnel in<br /> front of the instance. During local development, <code>n8n</code> started through a tunnel works as well.</p> <p>The empirio.ai node itself has no such requirement — it makes outbound calls only and works on any n8n<br /> instance with internet access.</p> <h2>Example workflow</h2> <p>A weekly CSV of a survey’s responses. <strong>Export Responses</strong> answers with a signed <code>download_url</code><br /> rather than the bytes, so a second step fetches it — that keeps a large export out of the workflow’s<br /> memory and lets the URL be passed to storage or email instead.</p> <p>Replace <code>YOUR<em>SURVEY</em>ID</code> with the ID from <strong>Survey → Get Many</strong>, or pick the survey from the list in<br /> the node. Paste this into a new workflow with <strong>Ctrl/Cmd+V</strong>:</p> <pre><code>{ "name": "empirio.ai u00b7 weekly CSV of new responses", "nodes": [ { "parameters": { "rule": { "interval": [ { "field": "weeks" } ] } }, "id": "a1", "name": "Every Monday", "type": "n8n-nodes-base.scheduleTrigger", "typeVersion": 1.2, "position": [0, 0] }, { "parameters": { "resource": "response", "operation": "exportResponses", "surveyId": { "__rl": true, "mode": "list", "value": "YOUR<em>SURVEY</em>ID" }, "format": "csv", "options": { "includeIncompleteParticipations": false } }, "id": "a2", "name": "Export responses", "type": "CUSTOM.empirioAi", "typeVersion": 1, "position": [220, 0], "credentials": { "empirioAiApi": { "id": "1", "name": "empirio.ai account" } } }, { "parameters": { "url": "={{ $json.download_url }}", "options": { "response": { "response": { "responseFormat": "file" } } } }, "id": "a3", "name": "Download the file", "type": "n8n-nodes-base.httpRequest", "typeVersion": 4.2, "position": [440, 0] } ], "connections": { "Every Monday": { "main": [ [ { "node": "Export responses", "type": "main", "index": 0 } ] ] }, "Export responses": { "main": [ [ { "node": "Download the file", "type": "main", "index": 0 } ] ] } } } </code></pre> <p>For charts instead of raw data, swap the operation for <strong>Export Charts</strong> and pick one of its six<br /> formats; everything else stays as it is.</p> <h2>Rate limits and idempotency</h2> <p>The empirio.ai API allows 60 read and 20 write requests per minute <strong>per user account</strong>, not per<br /> token — the budget is keyed on the account, so two n8n instances, or an n8n workflow and a Zap,<br /> running under the same account share one window. When a limit is hit the API answers <code>429</code> with a<br /> <code>Retry-After</code> header.</p> <p><strong><code>Retry On Fail</code> cannot recover from a <code>429</code>.</strong> n8n caps the node at 5 tries with at most 5 seconds<br /> between them, and it does not read <code>Retry-After</code>, so the longest backoff it can produce is about<br /> 20 seconds against a window that lasts 60. A saturated window outlives every retry, and the node<br /> fails anyway. Space the calls out instead: a <code>Wait</code> node, a smaller batch size, or a schedule that<br /> does not fire every workflow at once. Retry On Fail is still worth enabling for transient network<br /> and <code>5xx</code> failures, which it does fix.</p> <h3>Idempotency</h3> <p>Every write automatically carries an <code>Idempotency-Key</code>, and the API applies a repeated key at most<br /> once within 24 hours. The editor contains no idempotency input. Saved <code>idempotencyKey</code> values remain<br /> available to the runtime as hidden parameters.</p> <p>By default the node derives that key from the execution itself, as<br /> <code>n8n-<execution>-<node>-<run>-<item></code> — the execution ID, the node, the pass over its input and the<br /> item index. Two consequences worth knowing:</p> <li><strong>A retry of one write reuses its key.</strong> All of <code>Retry On Fail</code>‘s attempts belong to the same</li> <p> execution, node, pass and item, so the second attempt of a failed write is recognised as the same<br /> write and applied once. This matters most for <strong>Response → Delete</strong> in <em>Selected Rows</em> mode: row<br /> numbers are a rank that shifts the moment a delete lands, so a retry that counted as a new request<br /> would remove different rows.</p> <li><strong>Re-running the workflow is a new write.</strong> A manual re-run, a scheduled run and a second webhook</li> </ul> <p> delivery are new executions with new keys, and each is applied on its own.</p> <p>#### As an AI tool</p> <p>n8n runs a node attached to an <strong>AI Agent</strong> through a different loop than a workflow step, and that<br /> loop advances the run index on every retry attempt rather than fixing it for the write. The pass<br /> index therefore cannot tell “attempt 2 of one call” from “call 2”, so on this path the key is derived<br /> from the call’s own resolved parameters instead, as <code>n8n-<execution>-<node>-t<fingerprint></code>. Retries<br /> stay safe, which is the property that matters for the destructive operations. The trade-off: **two<br /> tool calls that ask for byte-identical writes inside one execution count as one write.** If an agent<br /> should be able to create two identical drafts in a single run, give it something to vary.</p> <p>Under an <strong>MCP Server Trigger</strong> there is no execution to derive from at all: n8n does not give the<br /> node an execution ID on that host, and there is nothing else that separates one MCP session from the<br /> next. Rather than mint a key that would look stable and in fact be _the same key for unrelated<br /> writes days apart_, the node falls back to a random key there. That means:</p> <p>> <strong>Writes made through an MCP Server Trigger are not retry-protected.</strong> Do not enable<br /> > <code>Retry On Fail</code> on <strong>Response → Delete</strong> in <em>Selected Rows</em> mode on that host — a retried attempt<br /> > is a second, independent delete against re-ranked rows.</p> <p>A saved key is bound to its request data for 24 hours: reusing it after the data changes fails<br /> with <code>idempotency<em>key</em>conflict</code>. A saved expression that resolves to <code>null</code>, <code>undefined</code> or a blank<br /> still fails the item, so it cannot silently lose its retry protection.</p> <h3>Failed authentications</h3> <p>Refused authentications are counted separately from the request budget, against **the credential that<br /> was presented**, at 120 a minute. A workflow whose API key has been revoked or mistyped exhausts that<br /> budget on its own and is answered <code>429</code> until the window rolls over; nothing else is affected.</p> <p>The bucket is the credential rather than the caller’s address, and that is the part that matters on<br /> <strong>n8n Cloud</strong>: every workflow on the platform leaves from a small pool of shared addresses, so a<br /> budget keyed on the address would let one tenant’s stale key lock out everyone else’s working ones.<br /> Yours is charged to your credential and to nothing else.</p> <h2>Compatibility</h2> <p>Developed and tested against n8n 2.35.7. The package declares <code>engines.node >= 20</code>, which is what<br /> its own code needs; n8n’s minimum is higher (2.x asks for Node 22) and is what actually applies at<br /> runtime.</p> <h2>License</h2> <p><a href="LICENSE.md">MIT</a></p> <h3>Partial logic-rule updates</h3> <p>In Update Survey, a logic-rule update sends only the properties selected under <strong>What to Change</strong>.<br /> Changing just the action preserves the rule’s question, condition, compared values and identity.<br /> <strong>End the Survey</strong> removes the target reference. A targeted action requires a target question.<br /> Compared-value dropdowns use the rule’s current question unless a new question is explicitly selected.<br /> The forms do not copy an editor-time snapshot into the update.</p> <p>n8n and Zapier allow several properties to be selected together. Make selects a property group per<br /> row; <strong>Question, condition and values</strong> updates a complete comparison together. Other properties<br /> remain unchanged. An incompatible comparison is rejected by the API.</p> <p>The n8n examples <code>update-survey-builder.json</code> and <code>update-survey-edge-cases.json</code> in<br /> <code>integrations/n8n/examples/</code> create fresh draft surveys and check persisted results through Get<br /> steps and assertions. They cover question and option CRUD, reordering, matrix columns,<br /> translations, partial rule edits, invalid orders, atomic rejection and idempotency. Requests are<br /> paced to respect the API budget; run the workflows sequentially. They leave drafts for inspection<br /> and never publish them. The examples use the local development node type <code>CUSTOM.empirioAi</code>;<br /> select a credential pointing to the local API before running them.</p> </div> </main> <!-- Sidebar --> <aside class="node-single-sidebar"> <!-- Keywords --> <!-- Install --> <div class="node-sidebar-card"> <h3>Install</h3> <div class="node-install-wrap"> <code class="node-install-cmd">npm install n8n-nodes-empirio-ai</code> <button class="copy-btn" data-copy="npm install n8n-nodes-empirio-ai" title="Copy to clipboard"> <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" width="16" height="16"> <rect x="9" y="9" width="13" height="13" rx="2" ry="2"/> <path d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1"/> </svg> </button> </div> </div> <!-- This Week's Favorite Nodes --> <div class="node-sidebar-card node-sidebar-weekly"> <h3 class="weekly-favorites-title">This Week's Favorites in <a href="https://communitynodes.com/category/analytics/" class="node-filter-pill cat-analytics">Analytics</a></h3> <div class="weekly-top-list"> <a href="https://communitynodes.com/n8n-nodes-ancoremate/" class="sidebar-carousel-card"> <div class="sidebar-carousel-icon"> <img src="https://communitynodes.com/wp-content/themes/v1/assets/img/communiynodes_icon.svg" alt="" class="fallback-icon"> </div> <div class="sidebar-carousel-info"> <span class="sidebar-carousel-name"> AncoreMateApps </span> <span class="sidebar-carousel-downloads"> <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" width="12" height="12"> <path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"/> <polyline points="7 10 12 15 17 10"/> <line x1="12" y1="15" x2="12" y2="3"/> </svg> 6,574/wk </span> </div> </a> <a href="https://communitynodes.com/n8n-nodes-powerbi/" class="sidebar-carousel-card"> <div class="sidebar-carousel-icon"> <img src="https://raw.githubusercontent.com/And-Rochaa/n8n-nodes-powerbi/master/nodes/PowerBI/powerbi.svg" alt=""> </div> <div class="sidebar-carousel-info"> <span class="sidebar-carousel-name"> Power BI </span> <span class="sidebar-carousel-downloads"> <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" width="12" height="12"> <path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"/> <polyline points="7 10 12 15 17 10"/> <line x1="12" y1="15" x2="12" y2="3"/> </svg> 3,142/wk </span> </div> </a> <a href="https://communitynodes.com/n8n-nodes-serpstat/" class="sidebar-carousel-card"> <div class="sidebar-carousel-icon"> <img src="https://raw.githubusercontent.com/alex-danilin/n8n-nodes-serpstat/master/nodes/Serpstat/serpstat.svg" alt=""> </div> <div class="sidebar-carousel-info"> <span class="sidebar-carousel-name"> Serpstat </span> <span class="sidebar-carousel-downloads"> <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" width="12" height="12"> <path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"/> <polyline points="7 10 12 15 17 10"/> <line x1="12" y1="15" x2="12" y2="3"/> </svg> 2,106/wk </span> </div> </a> </div> </div> <!-- Related Nodes --> <div class="node-sidebar-card node-sidebar-related"> <h3 class="related-nodes-title">More in <a href="https://communitynodes.com/category/analytics/" class="node-filter-pill cat-analytics">Analytics</a></h3> <div class="sidebar-carousel-nav"> <button class="sidebar-carousel-btn sidebar-carousel-prev" aria-label="Previous"> <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" width="16" height="16"> <path d="M15 18l-6-6 6-6"/> </svg> </button> <div class="sidebar-carousel-dots"></div> <button class="sidebar-carousel-btn sidebar-carousel-next" aria-label="Next"> <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" width="16" height="16"> <path d="M9 18l6-6-6-6"/> </svg> </button> </div> <div class="sidebar-carousel"> <div class="sidebar-carousel-track"> <a href="https://communitynodes.com/reportei-n8n-nodes-reportei/" class="sidebar-carousel-card"> <div class="sidebar-carousel-icon"> <img src="https://raw.githubusercontent.com/reportei/reportei-n8n-node/master/nodes/Reportei/reportei.svg" alt=""> </div> <div class="sidebar-carousel-info"> <span class="sidebar-carousel-name"> Reportei </span> <span class="sidebar-carousel-downloads"> <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" width="12" height="12"> <path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"/> <polyline points="7 10 12 15 17 10"/> <line x1="12" y1="15" x2="12" y2="3"/> </svg> 1,737/wk </span> </div> </a> <a href="https://communitynodes.com/n8n-nodes-llm-observability/" class="sidebar-carousel-card"> <div class="sidebar-carousel-icon"> <img src="https://communitynodes.com/wp-content/themes/v1/assets/img/communiynodes_icon.svg" alt="" class="fallback-icon"> </div> <div class="sidebar-carousel-info"> <span class="sidebar-carousel-name"> AI Agent Langfuse </span> <span class="sidebar-carousel-downloads"> <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" width="12" height="12"> <path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"/> <polyline points="7 10 12 15 17 10"/> <line x1="12" y1="15" x2="12" y2="3"/> </svg> 214/wk </span> </div> </a> <a href="https://communitynodes.com/ceki-n8n-nodes-ceki/" class="sidebar-carousel-card"> <div class="sidebar-carousel-icon"> <img src="https://communitynodes.com/wp-content/themes/v1/assets/img/communiynodes_icon.svg" alt="" class="fallback-icon"> </div> <div class="sidebar-carousel-info"> <span class="sidebar-carousel-name"> Browser Ceki </span> <span class="sidebar-carousel-downloads"> <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" width="12" height="12"> <path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"/> <polyline points="7 10 12 15 17 10"/> <line x1="12" y1="15" x2="12" y2="3"/> </svg> 1,753/wk </span> </div> </a> <a href="https://communitynodes.com/n8n-nodes-dataforseo/" class="sidebar-carousel-card"> <div class="sidebar-carousel-icon"> <img src="https://communitynodes.com/wp-content/themes/v1/assets/img/communiynodes_icon.svg" alt="" class="fallback-icon"> </div> <div class="sidebar-carousel-info"> <span class="sidebar-carousel-name"> DataForSEO </span> <span class="sidebar-carousel-downloads"> <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" width="12" height="12"> <path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"/> <polyline points="7 10 12 15 17 10"/> <line x1="12" y1="15" x2="12" y2="3"/> </svg> 277/wk </span> </div> </a> <a href="https://communitynodes.com/langfuse-n8n-nodes-langfuse/" class="sidebar-carousel-card"> <div class="sidebar-carousel-icon"> <img src="https://raw.githubusercontent.com/langfuse/n8n-nodes-langfuse/master/nodes/Langfuse/langfuse.svg" alt=""> </div> <div class="sidebar-carousel-info"> <span class="sidebar-carousel-name"> Langfuse </span> <span class="sidebar-carousel-downloads"> <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" width="12" height="12"> <path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"/> <polyline points="7 10 12 15 17 10"/> <line x1="12" y1="15" x2="12" y2="3"/> </svg> 577/wk </span> </div> </a> <a href="https://communitynodes.com/n8n-nodes-google-search-console/" class="sidebar-carousel-card"> <div class="sidebar-carousel-icon"> <img src="https://raw.githubusercontent.com/mrazari/n8n-nodes-google-search-console/master/nodes/GoogleSearchConsole/googlesearchconsole.svg" alt=""> </div> <div class="sidebar-carousel-info"> <span class="sidebar-carousel-name"> Google Search Console </span> <span class="sidebar-carousel-downloads"> <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" width="12" height="12"> <path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"/> <polyline points="7 10 12 15 17 10"/> <line x1="12" y1="15" x2="12" y2="3"/> </svg> 627/wk </span> </div> </a> <a href="https://communitynodes.com/firefliesai-n8n-nodes-fireflies/" class="sidebar-carousel-card"> <div class="sidebar-carousel-icon"> <img src="https://raw.githubusercontent.com/firefliesai/n8n-nodes-fireflies/master/nodes/Fireflies/fireflies.svg" alt=""> </div> <div class="sidebar-carousel-info"> <span class="sidebar-carousel-name"> Fireflies </span> <span class="sidebar-carousel-downloads"> <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" width="12" height="12"> <path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"/> <polyline points="7 10 12 15 17 10"/> <line x1="12" y1="15" x2="12" y2="3"/> </svg> 531/wk </span> </div> </a> <a href="https://communitynodes.com/n8n-nodes-fingrid/" class="sidebar-carousel-card"> <div class="sidebar-carousel-icon"> <img src="https://communitynodes.com/wp-content/themes/v1/assets/img/communiynodes_icon.svg" alt="" class="fallback-icon"> </div> <div class="sidebar-carousel-info"> <span class="sidebar-carousel-name"> Fingrid </span> <span class="sidebar-carousel-downloads"> <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" width="12" height="12"> <path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"/> <polyline points="7 10 12 15 17 10"/> <line x1="12" y1="15" x2="12" y2="3"/> </svg> 1,646/wk </span> </div> </a> <a href="https://communitynodes.com/n8n-nodes-duckdb-quack/" class="sidebar-carousel-card"> <div class="sidebar-carousel-icon"> <img src="https://communitynodes.com/wp-content/themes/v1/assets/img/communiynodes_icon.svg" alt="" class="fallback-icon"> </div> <div class="sidebar-carousel-info"> <span class="sidebar-carousel-name"> DuckDB Client </span> <span class="sidebar-carousel-downloads"> <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" width="12" height="12"> <path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"/> <polyline points="7 10 12 15 17 10"/> <line x1="12" y1="15" x2="12" y2="3"/> </svg> 1,070/wk </span> </div> </a> </div> </div> </div> <!-- Popular Tutorials --> <div class="node-sidebar-card node-sidebar-related node-sidebar-tutorials"> <h3 class="related-nodes-title">Popular Tutorials</h3> <div class="sidebar-carousel-nav"> <button class="sidebar-carousel-btn sidebar-carousel-prev" aria-label="Previous"> <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" width="16" height="16"> <path d="M15 18l-6-6 6-6"/> </svg> </button> <div class="sidebar-carousel-dots"></div> <button class="sidebar-carousel-btn sidebar-carousel-next" aria-label="Next"> <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" width="16" height="16"> <path d="M9 18l6-6-6-6"/> </svg> </button> </div> <div class="sidebar-carousel"> <div class="sidebar-carousel-track"> <a href="https://communitynodes.com/tutorials/the-easiest-way-to-build-your-first-automation-n8n-tutorial/" class="sidebar-carousel-card tutorial-sidebar-card tutorial-card-stacked"> <div class="tutorial-card-thumbnail"> <img src="https://img.youtube.com/vi/ONgECvZNI3o/hqdefault.jpg" alt="The Easiest Way to Build Your First Automation (n8n Tutorial)"> </div> <div class="tutorial-card-title"> The Easiest Way to Build Your First Automation (n8n Tutorial) </div> </a> <a href="https://communitynodes.com/tutorials/from-zero-to-your-first-ai-agent-in-25-minutes-no-coding/" class="sidebar-carousel-card tutorial-sidebar-card tutorial-card-stacked"> <div class="tutorial-card-thumbnail"> <img src="https://img.youtube.com/vi/EH5jx5qPabU/hqdefault.jpg" alt="From Zero to Your First AI Agent in 25 Minutes (No Coding)"> </div> <div class="tutorial-card-title"> From Zero to Your First AI Agent in 25 Minutes (No Coding) </div> </a> <a href="https://communitynodes.com/tutorials/n8n-will-change-your-life-as-a-developer/" class="sidebar-carousel-card tutorial-sidebar-card tutorial-card-stacked"> <div class="tutorial-card-thumbnail"> <img src="https://img.youtube.com/vi/bS9R6aCVEzw/hqdefault.jpg" alt="n8n Will Change Your Life as a Developer"> </div> <div class="tutorial-card-title"> n8n Will Change Your Life as a Developer </div> </a> <a href="https://communitynodes.com/tutorials/i-built-a-marketing-team-with-1-ai-agent/" class="sidebar-carousel-card tutorial-sidebar-card tutorial-card-stacked"> <div class="tutorial-card-thumbnail"> <img src="https://img.youtube.com/vi/ldETapkr8Hg/hqdefault.jpg" alt="I Built a Marketing Team with 1 AI Agent"> </div> <div class="tutorial-card-title"> I Built a Marketing Team with 1 AI Agent </div> </a> <a href="https://communitynodes.com/tutorials/an-ai-agent-that-builds-teams-of-ai-agents/" class="sidebar-carousel-card tutorial-sidebar-card tutorial-card-stacked"> <div class="tutorial-card-thumbnail"> <img src="https://img.youtube.com/vi/4cQWJViybAQ/hqdefault.jpg" alt="An AI Agent That Builds Teams of AI Agents"> </div> <div class="tutorial-card-title"> An AI Agent That Builds Teams of AI Agents </div> </a> </div> </div> </div> </aside> </div> </div> </section> <!-- Related Nodes Carousel --> <section class="related-nodes-section"> <div class="container"> <div class="related-nodes-header"> <h2>More in Analytics</h2> <div class="carousel-nav"> <button class="carousel-btn carousel-prev" aria-label="Previous"> <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" width="20" height="20"> <path d="M15 18l-6-6 6-6"/> </svg> </button> <button class="carousel-btn carousel-next" aria-label="Next"> <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" width="20" height="20"> <path d="M9 18l6-6-6-6"/> </svg> </button> </div> </div> <div class="related-nodes-carousel"> <div class="carousel-track"> <a href="https://communitynodes.com/reportei-n8n-nodes-reportei/" class="carousel-card"> <div class="carousel-card-icon"> <img src="https://raw.githubusercontent.com/reportei/reportei-n8n-node/master/nodes/Reportei/reportei.svg" alt=""> </div> <div class="carousel-card-info"> <h4> Reportei </h4> <div class="carousel-card-meta"> <span class="carousel-card-version">v0.1.5</span> <span class="carousel-card-downloads"> <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" width="12" height="12"> <path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"/> <polyline points="7 10 12 15 17 10"/> <line x1="12" y1="15" x2="12" y2="3"/> </svg> 1,737 </span> </div> </div> </a> <a href="https://communitynodes.com/n8n-nodes-powerbi/" class="carousel-card"> <div class="carousel-card-icon"> <img src="https://raw.githubusercontent.com/And-Rochaa/n8n-nodes-powerbi/master/nodes/PowerBI/powerbi.svg" alt=""> </div> <div class="carousel-card-info"> <h4> Power BI </h4> <div class="carousel-card-meta"> <span class="carousel-card-version">v2.4.0</span> <span class="carousel-card-downloads"> <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" width="12" height="12"> <path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"/> <polyline points="7 10 12 15 17 10"/> <line x1="12" y1="15" x2="12" y2="3"/> </svg> 3,142 </span> </div> </div> </a> <a href="https://communitynodes.com/n8n-nodes-ancoremate/" class="carousel-card"> <div class="carousel-card-icon"> <img src="https://communitynodes.com/wp-content/themes/v1/assets/img/communiynodes_icon.svg" alt="" class="fallback-icon"> </div> <div class="carousel-card-info"> <h4> AncoreMateApps </h4> <div class="carousel-card-meta"> <span class="carousel-card-version">v1.23.9</span> <span class="carousel-card-downloads"> <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" width="12" height="12"> <path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"/> <polyline points="7 10 12 15 17 10"/> <line x1="12" y1="15" x2="12" y2="3"/> </svg> 6,574 </span> </div> </div> </a> <a href="https://communitynodes.com/n8n-nodes-llm-observability/" class="carousel-card"> <div class="carousel-card-icon"> <img src="https://communitynodes.com/wp-content/themes/v1/assets/img/communiynodes_icon.svg" alt="" class="fallback-icon"> </div> <div class="carousel-card-info"> <h4> AI Agent Langfuse </h4> <div class="carousel-card-meta"> <span class="carousel-card-version">v0.10.40</span> <span class="carousel-card-downloads"> <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" width="12" height="12"> <path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"/> <polyline points="7 10 12 15 17 10"/> <line x1="12" y1="15" x2="12" y2="3"/> </svg> 214 </span> </div> </div> </a> <a href="https://communitynodes.com/ceki-n8n-nodes-ceki/" class="carousel-card"> <div class="carousel-card-icon"> <img src="https://communitynodes.com/wp-content/themes/v1/assets/img/communiynodes_icon.svg" alt="" class="fallback-icon"> </div> <div class="carousel-card-info"> <h4> Browser Ceki </h4> <div class="carousel-card-meta"> <span class="carousel-card-version">v0.2.35</span> <span class="carousel-card-downloads"> <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" width="12" height="12"> <path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"/> <polyline points="7 10 12 15 17 10"/> <line x1="12" y1="15" x2="12" y2="3"/> </svg> 1,753 </span> </div> </div> </a> <a href="https://communitynodes.com/n8n-nodes-dataforseo/" class="carousel-card"> <div class="carousel-card-icon"> <img src="https://communitynodes.com/wp-content/themes/v1/assets/img/communiynodes_icon.svg" alt="" class="fallback-icon"> </div> <div class="carousel-card-info"> <h4> DataForSEO </h4> <div class="carousel-card-meta"> <span class="carousel-card-version">v1.6.5</span> <span class="carousel-card-downloads"> <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" width="12" height="12"> <path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"/> <polyline points="7 10 12 15 17 10"/> <line x1="12" y1="15" x2="12" y2="3"/> </svg> 277 </span> </div> </div> </a> <a href="https://communitynodes.com/langfuse-n8n-nodes-langfuse/" class="carousel-card"> <div class="carousel-card-icon"> <img src="https://raw.githubusercontent.com/langfuse/n8n-nodes-langfuse/master/nodes/Langfuse/langfuse.svg" alt=""> </div> <div class="carousel-card-info"> <h4> Langfuse </h4> <div class="carousel-card-meta"> <span class="carousel-card-version">v0.2.3</span> <span class="carousel-card-downloads"> <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" width="12" height="12"> <path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"/> <polyline points="7 10 12 15 17 10"/> <line x1="12" y1="15" x2="12" y2="3"/> </svg> 577 </span> </div> </div> </a> <a href="https://communitynodes.com/n8n-nodes-serpstat/" class="carousel-card"> <div class="carousel-card-icon"> <img src="https://raw.githubusercontent.com/alex-danilin/n8n-nodes-serpstat/master/nodes/Serpstat/serpstat.svg" alt=""> </div> <div class="carousel-card-info"> <h4> Serpstat </h4> <div class="carousel-card-meta"> <span class="carousel-card-version">v0.10.0</span> <span class="carousel-card-downloads"> <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" width="12" height="12"> <path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"/> <polyline points="7 10 12 15 17 10"/> <line x1="12" y1="15" x2="12" y2="3"/> </svg> 2,106 </span> </div> </div> </a> </div> </div> </div> </section> <!-- Footer --> <footer id="c12s-footer" class="section-pt6b3"> <div class="container"> <div class="row"> <div class="col-md-12 text-center"> Copyright © 2026 Community Nodes </div> <div class="col-md-12 text-center"> <a href="/privacy-policy">Privacy Policy</a> | <a href="/terms-and-conditions">Terms and Conditions</a> </div> <div class="col-md-12 text-center footer-disclaimer"> communitynodes.com is not affiliated with, endorsed by, or sponsored by <a href="https://n8n.io" target="_blank" rel="noopener noreferrer">n8n</a> GmbH. </div> </div> </div> </footer> <button class="go-to-top" id="goToTop" aria-label="Go to top"> <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" width="24" height="24"> <path d="M18 15l-6-6-6 6"/> </svg> </button> <div class="notify-modal-overlay" id="bookmarkConfirmModal"> <div class="notify-modal" role="dialog" aria-modal="true" aria-labelledby="bookmarkConfirmTitle"> <button type="button" class="notify-modal-close" aria-label="Close"> <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"> <path d="M18 6L6 18M6 6l12 12"/> </svg> </button> <div style="display: flex; align-items: center;"> <div class="notify-modal-icon bookmark-confirm-icon"> <svg viewBox="0 0 24 24" fill="currentColor"> <path d="M19 21l-7-5-7 5V5a2 2 0 0 1 2-2h10a2 2 0 0 1 2 2z"/> </svg> </div> <h3 class="notify-modal-title" id="bookmarkConfirmTitle">Added to Favorites</h3> </div> <p class="notify-modal-desc"><strong id="bookmarkNodeName">This node</strong> has been added to your favorite nodes list.</p> <p class="notify-modal-desc" style="margin-bottom: 20px;">Would you like to get notified about updates, news and announcements for this node?</p> <div class="bookmark-confirm-actions"> <button type="button" class="bookmark-confirm-btn bookmark-confirm-yes" id="bookmarkConfirmYes">Yes, notify me</button> </div> <input type="hidden" id="bookmarkConfirmPostId" value=""> <input type="hidden" id="bookmarkConfirmPostTitle" value=""> </div> </div> <div class="notify-modal-overlay" id="notifyModal"> <div class="notify-modal" role="dialog" aria-modal="true" aria-labelledby="notifyModalTitle"> <button type="button" class="notify-modal-close" aria-label="Close"> <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"> <path d="M18 6L6 18M6 6l12 12"/> </svg> </button> <div class="notify-modal-header"> <div class="notify-modal-icon"> <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"> <path d="M18 8A6 6 0 0 0 6 8c0 7-3 9-3 9h18s-3-2-3-9"/> <path d="M13.73 21a2 2 0 0 1-3.46 0"/> </svg> </div> <div class="notify-modal-header-text"> <h3 class="notify-modal-title" id="notifyModalTitle">Get notified about updates</h3> <p class="notify-modal-desc">Enter your email to receive updates about <strong id="notifyNodeName">this node</strong>.</p> </div> </div> <input type="hidden" name="post_id" id="notifyPostId" value=""> <div class="notify-modal-cf7"> <div class="wpcf7 no-js" id="wpcf7-f6771-o1" lang="en-US" dir="ltr" data-wpcf7-id="6771"> <div class="screen-reader-response"><p role="status" aria-live="polite" aria-atomic="true"></p> <ul></ul></div> <form action="/empirio-ai-n8n-nodes-empirio-ai/#wpcf7-f6771-o1" method="post" class="wpcf7-form init" aria-label="Contact form" novalidate="novalidate" data-status="init"> <fieldset class="hidden-fields-container"><input type="hidden" name="_wpcf7" value="6771" /><input type="hidden" name="_wpcf7_version" value="6.1.4" /><input type="hidden" name="_wpcf7_locale" value="en_US" /><input type="hidden" name="_wpcf7_unit_tag" value="wpcf7-f6771-o1" /><input type="hidden" name="_wpcf7_container_post" value="0" /><input type="hidden" name="_wpcf7_posted_data_hash" value="" /> </fieldset> <p><label> Your email<br /> <span class="wpcf7-form-control-wrap" data-name="your-email"><input size="40" maxlength="400" class="wpcf7-form-control wpcf7-email wpcf7-validates-as-required wpcf7-text wpcf7-validates-as-email" autocomplete="email" aria-required="true" aria-invalid="false" value="" type="email" name="your-email" /></span> </label> </p> <p><span id="wpcf7-6ad1eccec551c-wrapper" class="wpcf7-form-control-wrap cf7apps-honeypot-wrap newsletter-website-wrap" data-cf7apps-honeypot="newsletter-website" style="position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap;border:0;"><input type="hidden" name="newsletter-website-random-hash" value="20261016"><input type="hidden" name="newsletter-website-time-token" value="MTc5MjE0MjU0Mg==.c7138dcab41885844c2945b0b9660cbbf612a2f40ecb36c2390bb55ce7938201"><label for="wpcf7-6ad1eccec551c-field" class="hp-message" >Please leave this field empty.</label><input id="wpcf7-6ad1eccec551c-field" class="wpcf7-form-control wpcf7-text" type="text" name="j1uq7doqf3fd" value="" autocomplete="new-password" tabindex="1000" data-cf7apps-tabindex="1000" /></span> </p> <p><input class="wpcf7-form-control wpcf7-submit has-spinner" type="submit" value="Submit" /> </p><p style="display: none !important;" class="akismet-fields-container" data-prefix="_wpcf7_ak_"><label>Δ<textarea name="_wpcf7_ak_hp_textarea" cols="45" rows="8" maxlength="100"></textarea></label><input type="hidden" id="ak_js_1" name="_wpcf7_ak_js" value="105"/><script> document.getElementById( "ak_js_1" ).setAttribute( "value", ( new Date() ).getTime() ); </script> </p><div class="wpcf7-response-output" aria-hidden="true"></div> </form> </div> </div> </div> </div> <div class="suggest-feature-modal-overlay" id="suggestFeatureModal"> <div class="suggest-feature-modal" role="dialog" aria-modal="true" aria-labelledby="suggestFeatureModalTitle"> <button type="button" class="suggest-feature-modal-close" aria-label="Close"> <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"> <path d="M18 6L6 18M6 6l12 12"/> </svg> </button> <div class="suggest-feature-modal-header"> <div class="suggest-feature-modal-icon"> <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"> <path d="M12 2v4M12 18v4M4.93 4.93l2.83 2.83M16.24 16.24l2.83 2.83M2 12h4M18 12h4M4.93 19.07l2.83-2.83M16.24 7.76l2.83-2.83"/> </svg> </div> <div class="suggest-feature-modal-header-text"> <h3 class="suggest-feature-modal-title" id="suggestFeatureModalTitle">Suggest A Feature</h3> <p class="suggest-feature-modal-desc">Have an idea to improve Community Nodes? We would love to hear it!</p> </div> </div> <div class="suggest-feature-cf7"> <div class="wpcf7 no-js" id="wpcf7-f13120-o2" lang="en-US" dir="ltr" data-wpcf7-id="13120"> <div class="screen-reader-response"><p role="status" aria-live="polite" aria-atomic="true"></p> <ul></ul></div> <form action="/empirio-ai-n8n-nodes-empirio-ai/#wpcf7-f13120-o2" method="post" class="wpcf7-form init" aria-label="Contact form" novalidate="novalidate" data-status="init"> <fieldset class="hidden-fields-container"><input type="hidden" name="_wpcf7" value="13120" /><input type="hidden" name="_wpcf7_version" value="6.1.4" /><input type="hidden" name="_wpcf7_locale" value="en_US" /><input type="hidden" name="_wpcf7_unit_tag" value="wpcf7-f13120-o2" /><input type="hidden" name="_wpcf7_container_post" value="0" /><input type="hidden" name="_wpcf7_posted_data_hash" value="" /> </fieldset> <p><label> Your name<br /> <span class="wpcf7-form-control-wrap" data-name="your-name"><input size="40" maxlength="400" class="wpcf7-form-control wpcf7-text wpcf7-validates-as-required" autocomplete="name" aria-required="true" aria-invalid="false" value="" type="text" name="your-name" /></span> </label> </p> <p><label> Your email<br /> <span class="wpcf7-form-control-wrap" data-name="your-email"><input size="40" maxlength="400" class="wpcf7-form-control wpcf7-email wpcf7-validates-as-required wpcf7-text wpcf7-validates-as-email" autocomplete="email" aria-required="true" aria-invalid="false" value="" type="email" name="your-email" /></span> </label> </p> <p><label> Subject<br /> <span class="wpcf7-form-control-wrap" data-name="your-subject"><input size="40" maxlength="400" class="wpcf7-form-control wpcf7-text wpcf7-validates-as-required" aria-required="true" aria-invalid="false" value="" type="text" name="your-subject" /></span> </label> </p> <p><label> Your message (optional)<br /> <span class="wpcf7-form-control-wrap" data-name="your-message"><textarea cols="40" rows="10" maxlength="2000" class="wpcf7-form-control wpcf7-textarea" aria-invalid="false" name="your-message"></textarea></span> </label> </p> <p><span id="wpcf7-6ad1eccec5f57-wrapper" class="wpcf7-form-control-wrap cf7apps-honeypot-wrap suggest-website-wrap" data-cf7apps-honeypot="suggest-website" style="position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap;border:0;"><input type="hidden" name="suggest-website-random-hash" value="20261016"><input type="hidden" name="suggest-website-time-token" value="MTc5MjE0MjU0Mg==.eae506424b71a6096126b2cc9a369463fa5f55f12b7d041397d7706f8815b602"><label for="wpcf7-6ad1eccec5f57-field" class="hp-message" >Please leave this field empty.</label><input id="wpcf7-6ad1eccec5f57-field" class="wpcf7-form-control wpcf7-text" type="text" name="4sa0x32gfczz" value="" autocomplete="new-password" tabindex="1000" data-cf7apps-tabindex="1000" /></span> </p> <p><span class="wpcf7-form-control-wrap" data-name="spam-check"><label><span class="wpcf7-quiz-label">What is four plus three? (a digit)</span> <input size="40" class="wpcf7-form-control wpcf7-quiz" autocomplete="off" aria-required="true" aria-invalid="false" type="text" name="spam-check" /></label><input type="hidden" name="_wpcf7_quiz_answer_spam-check" value="9b1894184327b58f1fb89e5b8804fe07" /></span> </p> <p><input class="wpcf7-form-control wpcf7-submit has-spinner" type="submit" value="Submit" /> </p><p style="display: none !important;" class="akismet-fields-container" data-prefix="_wpcf7_ak_"><label>Δ<textarea name="_wpcf7_ak_hp_textarea" cols="45" rows="8" maxlength="100"></textarea></label><input type="hidden" id="ak_js_2" name="_wpcf7_ak_js" value="162"/><script> document.getElementById( "ak_js_2" ).setAttribute( "value", ( new Date() ).getTime() ); </script> </p><div class="wpcf7-response-output" aria-hidden="true"></div> </form> </div> </div> </div> </div> <script type="speculationrules"> {"prefetch":[{"source":"document","where":{"and":[{"href_matches":"/*"},{"not":{"href_matches":["/wp-*.php","/wp-admin/*","/wp-content/uploads/*","/wp-content/*","/wp-content/plugins/*","/wp-content/themes/v1/*","/*\\?(.+)"]}},{"not":{"selector_matches":"a[rel~=\"nofollow\"]"}},{"not":{"selector_matches":".no-prefetch, .no-prefetch a"}}]},"eagerness":"conservative"}]} </script> <!-- Google Tag Manager (noscript) snippet added by Site Kit --> <noscript> <iframe src="https://www.googletagmanager.com/ns.html?id=GTM-PPDPLPHJ" height="0" width="0" style="display:none;visibility:hidden"></iframe> </noscript> <!-- End Google Tag Manager (noscript) snippet added by Site Kit --> <script id="wp-hooks-js" src="https://communitynodes.com/wp-includes/js/dist/hooks.min.js?ver=f0f188028580e8dc1255"></script> <script id="wp-i18n-js" src="https://communitynodes.com/wp-includes/js/dist/i18n.min.js?ver=1dfe7db3940c23ea9216"></script> <script id="wp-i18n-js-after"> wp.i18n.setLocaleData( { 'text direction\u0004ltr': [ 'ltr' ] } ); //# sourceURL=wp-i18n-js-after </script> <script id="swv-js" src="https://communitynodes.com/wp-content/plugins/contact-form-7/includes/swv/js/index.js?ver=6.1.4"></script> <script id="contact-form-7-js-before"> var wpcf7 = { "api": { "root": "https:\/\/communitynodes.com\/wp-json\/", "namespace": "contact-form-7\/v1" } }; //# sourceURL=contact-form-7-js-before </script> <script id="contact-form-7-js" src="https://communitynodes.com/wp-content/plugins/contact-form-7/includes/js/index.js?ver=6.1.4"></script> <script id="cf7apps-honeypot-refill-js-extra"> var cf7appsHoneypotRefill = {"forceRefillOnInit":""}; //# sourceURL=cf7apps-honeypot-refill-js-extra </script> <script id="cf7apps-honeypot-refill-js" src="https://communitynodes.com/wp-content/plugins/contact-form-7-honeypot/legacy-honeypot/includes/js/honeypot-refill.js?ver=3.7.1"></script> <script id="c12s-main-js-extra"> var loadmore_params = {"ajaxurl":"https://communitynodes.com/wp-admin/admin-ajax.php","posts":"{\"page\":0,\"name\":\"empirio-ai-n8n-nodes-empirio-ai\",\"error\":\"\",\"m\":\"\",\"p\":0,\"post_parent\":\"\",\"subpost\":\"\",\"subpost_id\":\"\",\"attachment\":\"\",\"attachment_id\":0,\"pagename\":\"\",\"page_id\":0,\"second\":\"\",\"minute\":\"\",\"hour\":\"\",\"day\":0,\"monthnum\":0,\"year\":0,\"w\":0,\"category_name\":\"\",\"tag\":\"\",\"cat\":\"\",\"tag_id\":\"\",\"author\":\"\",\"author_name\":\"\",\"feed\":\"\",\"tb\":\"\",\"paged\":0,\"meta_key\":\"\",\"meta_value\":\"\",\"preview\":\"\",\"s\":\"\",\"sentence\":\"\",\"title\":\"\",\"fields\":\"all\",\"menu_order\":\"\",\"embed\":\"\",\"category__in\":[],\"category__not_in\":[],\"category__and\":[],\"post__in\":[],\"post__not_in\":[],\"post_name__in\":[],\"tag__in\":[],\"tag__not_in\":[],\"tag__and\":[],\"tag_slug__in\":[],\"tag_slug__and\":[],\"post_parent__in\":[],\"post_parent__not_in\":[],\"author__in\":[],\"author__not_in\":[],\"search_columns\":[],\"post_type\":\"community_nodes\",\"community_nodes\":\"empirio-ai-n8n-nodes-empirio-ai\",\"ignore_sticky_posts\":false,\"suppress_filters\":false,\"cache_results\":true,\"update_post_term_cache\":true,\"update_menu_item_cache\":false,\"lazy_load_term_meta\":true,\"update_post_meta_cache\":true,\"posts_per_page\":10,\"nopaging\":false,\"comments_per_page\":\"50\",\"no_found_rows\":false,\"order\":\"DESC\"}","current_page":"1","max_page":"0","nonce":"2019aeb034"}; var c12s_bookmarks = {"cookie_name":"c12s_bookmarks","cookie_days":"365","labels":{"bookmark":"Add to bookmarks","bookmarked":"Remove from bookmarks","added":"Added to bookmarks","removed":"Removed from bookmarks"}}; //# sourceURL=c12s-main-js-extra </script> <script id="c12s-main-js" src="https://communitynodes.com/wp-content/themes/v1/assets/js/main.js?ver=1766898571"></script> <script id="googlesitekit-events-provider-contact-form-7-js" src="https://communitynodes.com/wp-content/plugins/google-site-kit/dist/assets/js/googlesitekit-events-provider-contact-form-7-fac2a8013ba5ba355cd0.js" defer></script> <script id="googlesitekit-events-provider-content-events-js-before"> window._googlesitekit = window._googlesitekit || {}; window._googlesitekit.contentEvents = {"postID":28178,"isReadableSinglePost":false,"hasVimeoEmbed":false,"wordCount":0,"estimatedReadTimeSeconds":0,"isLastPageOfMultiPagePost":false,"readTimeThresholdPercent":85,"minimumReadTimeSeconds":5}; //# sourceURL=googlesitekit-events-provider-content-events-js-before </script> <script id="googlesitekit-events-provider-content-events-js" src="https://communitynodes.com/wp-content/plugins/google-site-kit/dist/assets/js/googlesitekit-events-provider-content-events-6b04077d00f0d606ba42.js" defer></script> <script id="wp-emoji-settings" type="application/json"> {"baseUrl":"https://s.w.org/images/core/emoji/17.0.2/72x72/","ext":".png","svgUrl":"https://s.w.org/images/core/emoji/17.0.2/svg/","svgExt":".svg","source":{"concatemoji":"https://communitynodes.com/wp-includes/js/wp-emoji-release.min.js?ver=7.1.3"}} </script> <script type="module"> /*! This file is auto-generated */ var e="script#wp-emoji-settings",t=document.querySelector(e);if(!(t instanceof HTMLScriptElement))throw new Error("Element missing: "+e);const r=JSON.parse(t.text),s=(window._wpemojiSettings=r,"wpEmojiSettingsSupports"),o=["flag","emoji"];function i(e){try{var t={supportTests:e,timestamp:(new Date).valueOf()};sessionStorage.setItem(s,JSON.stringify(t))}catch(e){}}function c(e,t,n){e.clearRect(0,0,e.canvas.width,e.canvas.height),e.fillText(t,0,0);t=new Uint32Array(e.getImageData(0,0,e.canvas.width,e.canvas.height).data);e.clearRect(0,0,e.canvas.width,e.canvas.height),e.fillText(n,0,0);const r=new Uint32Array(e.getImageData(0,0,e.canvas.width,e.canvas.height).data);return t.every((e,t)=>e===r[t])}function p(e,t){e.clearRect(0,0,e.canvas.width,e.canvas.height),e.fillText(t,0,0);var n=e.getImageData(16,16,1,1);for(let e=0;e<n.data.length;e++)if(0!==n.data[e])return!1;return!0}function u(e,t,n,r){switch(t){case"flag":return n(e,"\ud83c\udff3\ufe0f\u200d\u26a7\ufe0f","\ud83c\udff3\ufe0f\u200b\u26a7\ufe0f")?!1:!n(e,"\ud83c\udde8\ud83c\uddf6","\ud83c\udde8\u200b\ud83c\uddf6")&&!n(e,"\ud83c\udff4\udb40\udc67\udb40\udc62\udb40\udc65\udb40\udc6e\udb40\udc67\udb40\udc7f","\ud83c\udff4\u200b\udb40\udc67\u200b\udb40\udc62\u200b\udb40\udc65\u200b\udb40\udc6e\u200b\udb40\udc67\u200b\udb40\udc7f");case"emoji":return!r(e,"\ud83e\u1fac8")}return!1}function f(e,t,n,r){let a;const s=(a="undefined"!=typeof WorkerGlobalScope&&self instanceof WorkerGlobalScope?new OffscreenCanvas(300,150):document.createElement("canvas")).getContext("2d",{willReadFrequently:!0}),o=(s.textBaseline="top",s.font="600 32px Arial",{});return e.forEach(e=>{o[e]=t(s,e,n,r)}),o}function a(e){var t=document.createElement("script");t.src=e,t.defer=!0,document.head.appendChild(t)}r.supports={everything:!0,everythingExceptFlag:!0},new Promise(t=>{let n=function(){try{var e=JSON.parse(sessionStorage.getItem(s));if("object"==typeof e&&"number"==typeof e.timestamp&&(new Date).valueOf()<e.timestamp+604800&&"object"==typeof e.supportTests)return e.supportTests}catch(e){}return null}();if(!n){if("undefined"!=typeof Worker&&"undefined"!=typeof OffscreenCanvas&&"undefined"!=typeof URL&&URL.createObjectURL&&"undefined"!=typeof Blob)try{var e="postMessage("+f.toString()+"("+[JSON.stringify(o),u.toString(),c.toString(),p.toString()].join(",")+"));",r=new Blob([e],{type:"text/javascript"});const a=new Worker(URL.createObjectURL(r),{name:"wpTestEmojiSupports"});return void(a.onmessage=e=>{i(n=e.data),a.terminate(),t(n)})}catch(e){}i(n=f(o,u,c,p))}t(n)}).then(e=>{for(const n in e)r.supports[n]=e[n],r.supports.everything=r.supports.everything&&r.supports[n],"flag"!==n&&(r.supports.everythingExceptFlag=r.supports.everythingExceptFlag&&r.supports[n]);var t;r.supports.everythingExceptFlag=r.supports.everythingExceptFlag&&!r.supports.flag,r.supports.everything||((t=r.source||{}).concatemoji?a(t.concatemoji):t.wpemoji&&t.twemoji&&(a(t.twemoji),a(t.wpemoji)))}); //# sourceURL=https://communitynodes.com/wp-includes/js/wp-emoji-loader.min.js </script> </body> </html>