How to handle "empty output" from HTTP Request nodes with conditional logic? (Clerk DB GET)

My current Problem :down_arrow:

[Disclaimer: I’m very new to n8n!]

I have a workflow that looks up users via HTTP Request to an external API (Clerk). Before, I just needed to know if they actually were customers of mine.

I did that using this expression: {{ $(‘Clerk Lookup’).all().length > 0 }}
That way if a User Object was returned, this was my “True” Path that I continued with downstream.

Now I actually want to handle the Users who are non-Customers (=NOT in my Clerk DB). Therefore I’m looking for a way to not only filter out the existing customers but also make the Data of the non-customers available downstream because I want to send them an email. This is where I am currently completely lost. (The middle part of the workflow, right after the two Clerk Lookup Nodes)

Fyi: I want to stick with the HTTP Request node because I can use n8n’s inbuilt Credentials features, with the Code Node I would have to hard-code my Clerk.com API (I believe).

{
  "nodes": [
    {
      "parameters": {
        "conditions": {
          "options": {
            "caseSensitive": true,
            "leftValue": "",
            "typeValidation": "strict",
            "version": 2
          },
          "conditions": [
            {
              "id": "aa375f4d-1152-4bca-b059-f7a889d44fca",
              "leftValue": "={{ $json.Punkte }}",
              "rightValue": 7,
              "operator": {
                "type": "number",
                "operation": "gte"
              }
            }
          ],
          "combinator": "and"
        },
        "options": {}
      },
      "type": "n8n-nodes-base.if",
      "typeVersion": 2.2,
      "position": [
        220,
        0
      ],
      "id": "00ad52a2-7a8d-4a51-bb6f-59c9215db92f",
      "name": "If"
    },
    {
      "parameters": {
        "url": "={{$json[\"render_url\"]}}",
        "options": {
          "response": {
            "response": {
              "responseFormat": "file",
              "outputPropertyName": "=data"
            }
          }
        }
      },
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.2,
      "position": [
        1360,
        -280
      ],
      "id": "ee3ada3f-f187-4138-9474-7c05c4eaabda",
      "name": "Download PDF"
    },
    {
      "parameters": {
        "method": "POST",
        "url": "https://api.templated.io/v1/render",
        "sendHeaders": true,
        "headerParameters": {
          "parameters": [
            {
              "name": "Authorization",
              "value": "Bearer YOUR_TOKEN_HERE"
            }
          ]
        },
        "sendBody": true,
        "specifyBody": "json",
        "jsonBody": "={\n  \"template\": \"f7a62531-9bb8-4119-b421-d16e8bb8360c\",\n  \"format\": \"pdf\",\n  \"layers\": {\n    \"name\":  { \"text\": \"{{ $json['Dein Name (erscheint auf dem Zertifikat) '] }}\" },\n    \"title\": { \"text\": \"{{ $json['Fortbildungstitel'] }}\" },\n    \"date\":  { \"text\": \"{{ $json['Zeitstempel'].split(' ')[0] }}\" }\n  }\n}",
        "options": {}
      },
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.2,
      "position": [
        1160,
        -280
      ],
      "id": "8eb324dc-13ec-476b-abe8-2b7d07e2100f",
      "name": "Create PDF"
    },
    {
      "parameters": {
        "fromEmail": " <[email protected]>"
        "toEmail": "={{ $('Merge').item.json['Deine Email-Adresse'] }}",
        "subject": "Versuch's nochmal! ",
        "emailFormat": "text",
        "text": "Hey,\n\nDu hast leider die Mindestpunktzahl von 7/10 nicht erreicht. 🥲\n\nVersuch's einfach nochmal - bin sicher, dass es diesmal klappt! 😋",
        "options": {
          "appendAttribution": false
        }
      },
      "type": "n8n-nodes-base.emailSend",
      "typeVersion": 2.1,
      "position": [
        400,
        580
      ],
      "id": "bff58a61-ca80-4835-b1fb-514b0eb8f444",
      "name": "Nicht bestanden Email",
      "webhookId": "c01e0664-d220-42a4-a8e5-da1904da5b2c",
      "credentials": {
        "smtp": {
          "id": "AcJw452tbxtY5muD",
          "name": "SMTP account"
        }
      },
      "disabled": true
    },
    {
      "parameters": {
        "fromEmail": " <[email protected]>",
        "toEmail": "={{ $('Merge1').item.json['Deine Email-Adresse'] }}",
        "subject": "Hier ist Dein Zertifikat! 📄🎖️",
        "html": "Yay! Du hast das CME-Quiz bestanden! 🥳\n\nIm Anhang findest Du das Zertifikat zum Herunterladen ⬇️",
        "options": {
          "appendAttribution": false,
          "attachments": "=data"
        }
      },
      "type": "n8n-nodes-base.emailSend",
      "typeVersion": 2.1,
      "position": [
        1740,
        -280
      ],
      "id": "234bf9b0-a539-4282-9d3b-8cd9b7598932",
      "name": "Bestanden Email",
      "webhookId": "3dda19e8-f145-44be-afa5-3b1eacdcfcc7",
      "credentials": {
        "smtp": {
          "id": "AcJw452tbxtY5muD",
          "name": "SMTP account"
        }
      }
    },
    {
      "parameters": {
        "url": "https://api.clerk.com/v1/users",
        "authentication": "genericCredentialType",
        "genericAuthType": "httpHeaderAuth",
        "sendQuery": true,
        "queryParameters": {
          "parameters": [
            {
              "name": "email_address",
              "value": "={{ $json['Deine Email-Adresse'] }}"
            }
          ]
        },
        "options": {}
      },
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.2,
      "position": [
        480,
        -20
      ],
      "id": "7b4a4f86-efbb-4607-950e-93085e5a054b",
      "name": "Clerk Lookup",
      "alwaysOutputData": true,
      "credentials": {
        "httpHeaderAuth": {
          "id": "swBdRbw3i8CwGhkk",
          "name": "Header Auth account"
        }
      }
    },
    {
      "parameters": {
        "conditions": {
          "options": {
            "caseSensitive": true,
            "leftValue": "",
            "typeValidation": "strict",
            "version": 2
          },
          "conditions": [
            {
              "id": "20bdc076-bf81-437a-aa8f-d509ea54a730",
              "leftValue": "={{ $json.id ? 1 : 0 }}",
              "rightValue": 0,
              "operator": {
                "type": "number",
                "operation": "gt"
              }
            }
          ],
          "combinator": "and"
        },
        "options": {}
      },
      "type": "n8n-nodes-base.if",
      "typeVersion": 2.2,
      "position": [
        700,
        80
      ],
      "id": "19397c5f-9f7c-4a50-9627-ea66704ee7b0",
      "name": "If1",
      "alwaysOutputData": false
    },
    {
      "parameters": {
        "method": "PATCH",
        "url": "=https://api.clerk.com/v1/users/{{$json.userId}}",
        "authentication": "genericCredentialType",
        "genericAuthType": "httpHeaderAuth",
        "sendBody": true,
        "specifyBody": "json",
        "jsonBody": "={{ $json.patch }}",
        "options": {}
      },
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.2,
      "position": [
        1800,
        80
      ],
      "id": "1990ee1a-1e1a-4149-9d29-6d932cd5062d",
      "name": "HTTP Request",
      "credentials": {
        "httpHeaderAuth": {
          "id": "swBdRbw3i8CwGhkk",
          "name": "Header Auth account"
        }
      }
    },
    {
      "parameters": {
        "numberInputs": 10
      },
      "type": "n8n-nodes-base.merge",
      "typeVersion": 3.1,
      "position": [
        0,
        0
      ],
      "id": "52cdef6b-cccd-481c-a0dc-11d1b329ea7b",
      "name": "Merge"
    },
    {
      "parameters": {
        "content": "## Clerk DB Update\n\n- Patched das Clerk User Object mit den Änderungen aus dem Prepare Clerk Update Node (+2 CME Punkte)",
        "height": 180,
        "width": 230
      },
      "type": "n8n-nodes-base.stickyNote",
      "typeVersion": 1,
      "position": [
        1760,
        220
      ],
      "id": "2e187d97-4ad1-48f0-8855-dee2477e6ce3",
      "name": "Sticky Note"
    },
    {
      "parameters": {
        "mode": "combine",
        "combineBy": "combineByPosition",
        "options": {}
      },
      "type": "n8n-nodes-base.merge",
      "typeVersion": 3.1,
      "position": [
        960,
        -380
      ],
      "id": "a5b1ef2f-a46d-4761-9aeb-7b6ca2edf542",
      "name": "Merge1"
    },
    {
      "parameters": {
        "jsCode": "// Flip gmail.com ↔ googlemail.com for all input items\nconst items = [];\n\nfor (const item of $input.all()) {\n  const email = item.json['Deine Email-Adresse'];\n  \n  if (email.includes('@gmail.com') || email.includes('@googlemail.com')) {\n    const flippedEmail = email.includes('@gmail.com') \n      ? email.replace('@gmail.com', '@googlemail.com')\n      : email.replace('@googlemail.com', '@gmail.com');\n    \n    console.log(`Flipping email from \"${email}\" to \"${flippedEmail}\"`);\n    \n    items.push({ \n      json: { \n        ...item.json, \n        'Deine Email-Adresse': flippedEmail \n      } \n    });\n  } else {\n    // Not a gmail variant, return original\n    items.push({ json: item.json });\n  }\n}\n\nreturn items;"
      },
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        420,
        240
      ],
      "name": "Email Flip",
      "id": "cfe5a9e3-6025-4184-a65d-b4bab605eb26"
    },
    {
      "parameters": {
        "url": "https://api.clerk.com/v1/users",
        "authentication": "genericCredentialType",
        "genericAuthType": "httpHeaderAuth",
        "sendQuery": true,
        "queryParameters": {
          "parameters": [
            {
              "name": "email_address",
              "value": "={{ $json['Deine Email-Adresse'] }}"
            }
          ]
        },
        "options": {}
      },
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.2,
      "position": [
        560,
        240
      ],
      "name": "Clerk Lookup 2",
      "id": "7c07346a-ad53-4e4a-b5ef-e23a80d1ef8d",
      "alwaysOutputData": true,
      "credentials": {
        "httpHeaderAuth": {
          "id": "swBdRbw3i8CwGhkk",
          "name": "Header Auth account"
        }
      }
    },
    {
      "parameters": {
        "conditions": {
          "options": {
            "caseSensitive": true,
            "leftValue": "",
            "typeValidation": "strict",
            "version": 2
          },
          "conditions": [
            {
              "id": "user-found-condition-2",
              "leftValue": "={{ Object.keys($json).length }}",
              "rightValue": 0,
              "operator": {
                "type": "number",
                "operation": "gt"
              }
            }
          ],
          "combinator": "and"
        },
        "options": {}
      },
      "type": "n8n-nodes-base.if",
      "typeVersion": 2.2,
      "position": [
        700,
        240
      ],
      "name": "If User Found 2",
      "id": "857fde85-8ba9-4e29-8998-4f4d485713a6"
    },
    {
      "parameters": {
        "assignments": {
          "assignments": [
            {
              "id": "13bfa4b9-aa31-4b4b-bf7d-83c83efe8c86",
              "name": "Dein Name (erscheint auf dem Zertifikat) ",
              "value": "={{ $json['Dein Name (erscheint auf dem Zertifikat)'] }}",
              "type": "string"
            },
            {
              "id": "ee00b96a-1c22-42a2-a5c1-3a8035754251",
              "name": "Deine Email-Adresse",
              "value": "={{ $json['Deine Email-Adresse'].trim() }}",
              "type": "string"
            },
            {
              "id": "acaa6909-25be-405a-8a33-61a0961b4d07",
              "name": "Fortbildungstitel",
              "value": "={{ $json.Fortbildungstitel }}",
              "type": "string"
            },
            {
              "id": "31241ed1-8cdb-4ce6-a759-8524d64018e4",
              "name": "Punkte",
              "value": "={{ $json.Punkte }}",
              "type": "number"
            },
            {
              "id": "d18e3708-7436-4f8e-b1e2-e00c0fc2b9e6",
              "name": "Zeitstempel",
              "value": "={{ $json.Zeitstempel }}",
              "type": "string"
            }
          ]
        },
        "options": {}
      },
      "type": "n8n-nodes-base.set",
      "typeVersion": 3.4,
      "position": [
        -400,
        160
      ],
      "id": "69873a73-7698-42c4-89ae-0b1b941dd8cf",
      "name": "Edit Fields4"
    },
    {
      "parameters": {
        "jsCode": "// Deduplicate Clerk user objects within the incoming items (across branches).\n// Keeps only the first occurrence of every unique user.id.\n\nconst seenIds = new Set();\nconst outputItems = [];\n\nfor (const item of $input.all()) {\n  const users = Array.isArray(item.json) ? item.json : [item.json];\n\n  for (const user of users) {\n    const id = user && user.id;\n    if (id && !seenIds.has(id)) {\n      seenIds.add(id);\n      outputItems.push({ json: user });\n    }\n  }\n}\n\nreturn outputItems;"
      },
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        1160,
        80
      ],
      "id": "877e116d-2e11-415a-8e4f-b8256e6035bb",
      "name": "Deduplicate Users",
      "alwaysOutputData": false
    },
    {
      "parameters": {},
      "type": "n8n-nodes-base.merge",
      "typeVersion": 3.1,
      "position": [
        1020,
        80
      ],
      "id": "c0b47d67-d45a-4b10-936b-a23ab628de30",
      "name": "Merge2"
    },
    {
      "parameters": {
        "jsCode": "// Build incremental Clerk PATCH payload (+2 CME points)\n// Input: full Clerk user object (from Deduplicate Users)\n// Output: { userId, patch }\n\nconst user       = $json;\nconst currentPM  = user.private_metadata || {};\nconst currentCP  = currentPM.cmePoints || {};\nconst newPoints  = (currentCP.internal || 0) + 2;\n\nreturn [{\n  json: {\n    userId: user.id,\n    patch: {\n      private_metadata: {\n        ...currentPM,\n        cmePoints: {\n          ...currentCP,\n          internal: newPoints,\n        },\n      },\n    },\n  },\n}];"
      },
      "id": "302634c2-3d35-447d-87a3-3640e463fd46",
      "name": "Prepare Clerk Update",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        1420,
        80
      ]
    },
    {
      "parameters": {
        "content": "## Prepare Clerk Update\n\n- Nimmt das Clerk-User-Objekt entgegen\n- Liest vorhandene private_metadata.cmePoints.internal\n- Addiert +2 (oder setzt auf 2, falls noch nicht vorhanden)\n- Baut einen kompakten PATCH-Body (json.patch) der _nur_ dieses Feld aktualisiert, ohne andere Felder zu überschreiben\n- Liefert außerdem die userId – beides geht an den HTTP-Request-Node",
        "height": 340,
        "width": 300
      },
      "type": "n8n-nodes-base.stickyNote",
      "typeVersion": 1,
      "position": [
        1340,
        220
      ],
      "id": "81329b7d-9726-4fa1-84eb-a2b6e634d7ab",
      "name": "Sticky Note4"
    },
    {
      "parameters": {
        "pollTimes": {
          "item": [
            {
              "mode": "everyMinute"
            }
          ]
        },
        "documentId": {
          "__rl": true,
          "value": "1VMEDmBNQXlhgMwK_mzkbiw4InQ8kBGanybs37K7zwPM",
          "mode": "list",
          "cachedResultName": "🎖️TL-CME-Quiz-Results",
          "cachedResultUrl": "https://docs.google.com/spreadsheets/d/1VMEDmBNQXlhgMwK_mzkbiw4InQ8kBGanybs37K7zwPM/edit?usp=drivesdk"
        },
        "sheetName": {
          "__rl": true,
          "value": 1528782937,
          "mode": "list",
          "cachedResultName": "Testtabelle(tobedeleted)",
          "cachedResultUrl": "https://docs.google.com/spreadsheets/d/1VMEDmBNQXlhgMwK_mzkbiw4InQ8kBGanybs37K7zwPM/edit#gid=1528782937"
        },
        "event": "rowAdded",
        "options": {}
      },
      "type": "n8n-nodes-base.googleSheetsTrigger",
      "typeVersion": 1,
      "position": [
        -600,
        160
      ],
      "id": "7efc5a62-d72a-468a-9f06-0432d4b69e2f",
      "name": "Testtabelle",
      "credentials": {
        "googleSheetsTriggerOAuth2Api": {
          "id": "fJADkBxk1SlYdgQN",
          "name": "Google Sheets Trigger account"
        }
      }
    },
    {
      "parameters": {
        "content": "## LATEST FIX:\n\nRun for each item eingestellt → Error (muss daher auf Once For All eingestellt bleiben)"
      },
      "type": "n8n-nodes-base.stickyNote",
      "typeVersion": 1,
      "position": [
        1360,
        -40
      ],
      "id": "5cf61892-57bc-4246-bb09-e2396619db05",
      "name": "Sticky Note5"
    }
  ],
  "connections": {
    "If": {
      "main": [
        [
          {
            "node": "Clerk Lookup",
            "type": "main",
            "index": 0
          },
          {
            "node": "Email Flip",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Nicht bestanden Email",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Download PDF": {
      "main": [
        []
      ]
    },
    "Create PDF": {
      "main": [
        [
          {
            "node": "Download PDF",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Clerk Lookup": {
      "main": [
        [
          {
            "node": "If1",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "If1": {
      "main": [
        [
          {
            "node": "Merge2",
            "type": "main",
            "index": 0
          }
        ],
        []
      ]
    },
    "Merge": {
      "main": [
        [
          {
            "node": "If",
            "type": "main",
            "index": 0
          },
          {
            "node": "Merge1",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Merge1": {
      "main": [
        [
          {
            "node": "Create PDF",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Email Flip": {
      "main": [
        [
          {
            "node": "Clerk Lookup 2",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Clerk Lookup 2": {
      "main": [
        [
          {
            "node": "If User Found 2",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "If User Found 2": {
      "main": [
        [
          {
            "node": "Merge2",
            "type": "main",
            "index": 1
          }
        ]
      ]
    },
    "Edit Fields4": {
      "main": [
        [
          {
            "node": "Merge",
            "type": "main",
            "index": 9
          }
        ]
      ]
    },
    "Deduplicate Users": {
      "main": [
        [
          {
            "node": "Merge1",
            "type": "main",
            "index": 1
          },
          {
            "node": "Prepare Clerk Update",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Merge2": {
      "main": [
        [
          {
            "node": "Deduplicate Users",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Prepare Clerk Update": {
      "main": [
        [
          {
            "node": "HTTP Request",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Testtabelle": {
      "main": [
        [
          {
            "node": "Edit Fields4",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  },
  "pinData": {},
  "meta": {
    "templateCredsSetupCompleted": true,
    "instanceId": "2b303379e16742056ad8f3cf60b01791e2b69b655811816a953611fd1a1c72c9"
  }
}

I’m still very new to automating and n8n, I even built a custom MCP Server to help me create workflows using natural language (Cursor/Claude). But even o3 Pro wasn’t able to figure out this specific problem, it often hallucinates abilities of certain nodes.

I’m using the Cloud version of n8n (v. 1.93.x)

Update:

Made it work (I think :crossed_fingers:t3:)

I merged the two ‘false’ paths of the If Nodes, both containing empty objects whenever a Non-Customer is detected. After that I make sure, that they really arent a customer → there have to be 2 empty objects for that to be true like so:

// Check if we have exactly 2 empty objects (both lookups failed)
const items = $input.all();
const emptyCount = items.filter(item =>
!item.json || Object.keys(item.json).length === 0
).length;

console.log(Found ${emptyCount} empty objects);

// If we have 2 empty objects, this means both Clerk lookups failed
if (emptyCount === 2) {
return [{ json: { isNonCustomer: true } }];
} else {
return ;
}

After that I am getting the User’s original Data via another Merge Node and voilá, the non-Customer can be addressed with an Email.

This topic was automatically closed 90 days after the last reply. New replies are no longer allowed.