mailneo

n8n community nodes for the Mailneo public API: a Mailneo node covering the v2 resources and a MailneoTrigger webhook node with HMAC signature verification.

Package Information

Released: 2 mo ago
Downloads: 0 weekly / 0 monthly
Latest Version: 0.1.2

Documentation

n8n-nodes-mailneo

n8n community nodes for the Mailneo public API (v2).

  • Mailneo — declarative node covering subscribers, lists, segments,
    contacts, campaigns, newsletters, templates, suppressions, analytics, email
    accounts, webhook endpoints and the credential's own identity/usage.
    Cursor pagination is handled for you ("Return All" or a limit — limits above
    the API's 100-per-page maximum fetch multiple pages). There are no send
    operations because the API exposes none yet.
  • Mailneo Trigger — starts workflows on Mailneo webhook events. It
    registers its own webhook endpoint on activation, deletes it on
    deactivation, and verifies the X-Mailneo-Signature HMAC on every
    delivery
    (timestamped, ±300 s tolerance, constant-time comparison).
    Unverifiable deliveries are rejected with 401 and never reach the workflow.

API docs: https://www.mailneo.co/documentation/api

Installation

Community node (recommended)

In n8n, open Settings > Community Nodes > Install and enter:

n8n-nodes-mailneo

Follow the n8n community nodes installation
guide
if
community nodes are not enabled on your instance.

Manual (self-hosted n8n)

Community nodes can also be installed into n8n's custom nodes directory:

mkdir -p ~/.n8n/custom
cd ~/.n8n/custom
npm install n8n-nodes-mailneo

# restart n8n

Credentials

Two credential types; every node instance has an Authentication switch.

Mailneo API (API key)

  1. In Mailneo: Settings > API Keys > Create key (mk_live_...).
  2. In n8n: create a Mailneo API credential and paste the key.
    It is sent as the x-api-key header. Leave Base URL alone unless you
    self-host Mailneo.

Mailneo OAuth2 API (OAuth 2.1 + PKCE)

Authorization-code with PKCE (S256) against https://api.mailneo.co — the
endpoints are the ones published by
https://api.mailneo.co/.well-known/oauth-authorization-server:

  • Authorize: https://api.mailneo.co/oauth/authorize
  • Token: https://api.mailneo.co/oauth/token
  • Dynamic Client Registration (RFC 7591): https://api.mailneo.co/oauth/register

With Use Dynamic Client Registration on (the default, supported by recent
n8n versions), n8n registers itself — no client ID needed. On older n8n
versions, turn it off and paste a client ID created in Mailneo (public
client; no client secret). Trim the requested scopes to what the workflow
needs; the default asks for every live scope.

Trigger notes

  • The endpoint's signing secret is returned by the API once, at creation,
    and kept in the workflow's static data. Deactivating and reactivating the
    workflow creates a fresh endpoint and secret.
  • Delivery is at-least-once and unordered. The output includes eventId
    (stable across retries and replays — deduplicate on it), event,
    deliveryId and isReplay.

Example workflow

Fetch enabled subscribers, keep the ones tagged vip, add them to a list:

{
  "name": "Mailneo: VIPs onto the launch list",
  "nodes": [
    {
      "parameters": {},
      "id": "b1f0f6a0-0000-4000-8000-000000000001",
      "name": "When clicking ‘Execute workflow’",
      "type": "n8n-nodes-base.manualTrigger",
      "typeVersion": 1,
      "position": [0, 0]
    },
    {
      "parameters": {
        "authentication": "apiKey",
        "resource": "subscriber",
        "operation": "getAll",
        "returnAll": true,
        "filters": { "status": "ENABLED" }
      },
      "id": "b1f0f6a0-0000-4000-8000-000000000002",
      "name": "Get Subscribers",
      "type": "n8n-nodes-mailneo.mailneo",
      "typeVersion": 1,
      "position": [220, 0],
      "credentials": {
        "mailneoApi": { "id": "REPLACE_ME", "name": "Mailneo API" }
      }
    },
    {
      "parameters": {
        "conditions": {
          "options": { "caseSensitive": true, "typeValidation": "strict", "version": 2 },
          "conditions": [
            {
              "leftValue": "={{ $json.tags }}",
              "rightValue": "vip",
              "operator": { "type": "array", "operation": "contains", "rightType": "any" }
            }
          ],
          "combinator": "and"
        }
      },
      "id": "b1f0f6a0-0000-4000-8000-000000000003",
      "name": "Keep VIPs",
      "type": "n8n-nodes-base.filter",
      "typeVersion": 2.2,
      "position": [440, 0]
    },
    {
      "parameters": {
        "authentication": "apiKey",
        "resource": "list",
        "operation": "addSubscribers",
        "listId": "REPLACE_WITH_LIST_ID",
        "subscriberIds": "={{ $json.id }}"
      },
      "id": "b1f0f6a0-0000-4000-8000-000000000004",
      "name": "Add to Launch List",
      "type": "n8n-nodes-mailneo.mailneo",
      "typeVersion": 1,
      "position": [660, 0],
      "credentials": {
        "mailneoApi": { "id": "REPLACE_ME", "name": "Mailneo API" }
      }
    }
  ],
  "connections": {
    "When clicking ‘Execute workflow’": {
      "main": [[{ "node": "Get Subscribers", "type": "main", "index": 0 }]]
    },
    "Get Subscribers": {
      "main": [[{ "node": "Keep VIPs", "type": "main", "index": 0 }]]
    },
    "Keep VIPs": {
      "main": [[{ "node": "Add to Launch List", "type": "main", "index": 0 }]]
    }
  },
  "settings": {}
}

Note: the API refuses to re-subscribe someone who unsubscribed from a list —
that membership row is the opt-out record. This is by design.

Development

npm ci
npm run typecheck   # tsc --noEmit over nodes, credentials and tests
npm test            # vitest: signature vectors, trigger lifecycle, pagination,
                    # credential assembly, and node-vs-OpenAPI conformance
npm run lint        # eslint-plugin-n8n-nodes-base (community ruleset)
npm run build       # tsc -> dist + icon assets

The conformance test reads openapi/openapi.json (a vendored snapshot of the
spec the Mailneo API publishes) and asserts, both directions, that the node's
routing and the spec agree — the node cannot silently drift from the API.

Discussion