AutomationFlowsWeb Scraping › Identify Medicine (tier 3 Fallback)

Identify Medicine (tier 3 Fallback)

Identify Medicine (tier 3 fallback). Uses httpRequest. Webhook trigger; 4 nodes.

Webhook trigger★★★★☆ complexity4 nodesHTTP Request
Web Scraping Trigger: Webhook Nodes: 4 Complexity: ★★★★☆ Added:

The workflow JSON

Copy or download the full n8n JSON below. Paste it into a new n8n workflow, add your credentials, activate. Full import guide →

Download .json
{
  "name": "Identify Medicine (tier 3 fallback)",
  "nodes": [
    {
      "parameters": {
        "httpMethod": "POST",
        "path": "identify-medicine",
        "responseMode": "lastNode",
        "options": {}
      },
      "type": "n8n-nodes-base.webhook",
      "typeVersion": 2.1,
      "position": [
        0,
        208
      ],
      "id": "im0001-0001-4a01-8a01-000000000001",
      "name": "Webhook"
    },
    {
      "parameters": {
        "jsCode": "// Build the OpenRouter identification request. Expects POST JSON: { name: \"aspirin\" }\n// This is tier 3 in the add-medicine form's auto-suggest -- only called after the\n// form has already checked (1) the user's own inventory and (2) the curated\n// kb_entries table and found nothing. Purpose: recognise real medicines the KB\n// doesn't cover yet (the \"aspirin\" gap), while still catching non-medicines\n// (the \"Mickey Mouse\" bug) -- a static list can only ever do the first half.\nconst b = ($input.first().json.body) || $input.first().json || {};\nconst name = String(b.name || '').trim();\n\n// Same controlled vocabulary as frontend/app.js SYMPTOM_TAGS + docs/api-contract.md.\n// Keep these two lists in sync if either changes.\nconst TAGS = 'fever, headache, cough, sore_throat, nausea, allergy, body_ache, muscle_ache, back_ache, sprain, inflammation, runny_nose, blocked_nose, sneezing, phlegm, itchy_eyes, hives, itch, rash, insect_bite, stomach_upset, stomach_cramps, diarrhoea, bloating, gas, indigestion, heartburn, acid_reflux, constipation, dehydration, cuts, scrapes, mouth_ulcer, dry_eyes, eye_irritation, scabies, lice, acne, cold_sore, fungal_infection, dry_skin, eczema, dandruff, warts, insomnia, joint_pain, tiredness, teething, mouth_pain, wound';\n\nconst systemPrompt = `You identify over-the-counter and common household medicines by name for a Singapore home-medicine cabinet app. You are a RECOGNITION step, not the main advice agent -- you only classify and tag, you never give dosing or safety advice here.\n\nGiven a medicine name (brand or generic/active-ingredient), decide:\n1. Is this a real, identifiable medicine? Judge generously for genuine typos or generic names (e.g. \"panadol\", \"paracetmol\"), but return false for anything that is clearly not a medicine at all -- a person's name, a cartoon character, gibberish, a random word, or anything you cannot map to a real drug with reasonable confidence. NEVER invent or guess a plausible-sounding but fictional medicine.\n2. If real, classify it into exactly ONE of these three buckets:\n   - ACUTE: an as-needed OTC medicine for a specific symptom (e.g. paracetamol, antihistamines, antacids).\n   - LONG_TERM: a daily maintenance medicine (blood pressure/diabetes/cholesterol/thyroid) or a finite prescribed course (e.g. an antibiotic) -- never a candidate answer to an acute-symptom question.\n   - WELLNESS: a vitamin/mineral/herbal supplement taken for an ongoing wellness goal (immune support, bone health, energy, joint health...) rather than a specific complaint -- also never a candidate answer to an acute-symptom question, but for a different reason (it's preventive, not symptom-relief).\n\nRespond with ONLY this JSON, no markdown, no extra text:\n{\"recognized\": boolean, \"canonical_name\": string|null, \"category\": string|null, \"symptom_tags\": string[], \"long_term\": boolean, \"general_wellness\": boolean, \"note\": string|null}\n\nRules:\n- \"symptom_tags\" may ONLY contain values from this exact list: ${TAGS}. Use as many as genuinely apply; drop anything that doesn't fit this list rather than inventing a new tag.\n- If \"long_term\" is true OR \"general_wellness\" is true, \"symptom_tags\" MUST be an empty array. A medicine is never both at once -- pick whichever bucket fits best.\n- \"category\" is a short lowercase word like \"painkiller\", \"antihistamine\", \"digestive\", \"topical\", \"cold\", or similar -- null if unsure.\n- \"canonical_name\" is the medicine's common name, properly capitalised (e.g. \"Aspirin\", not \"aspirin\"). Null if not recognized.\n- \"note\" is null unless there's something genuinely important to flag (e.g. \"Pharmacy-only in Singapore\" or \"Prescription antibiotic -- take exactly as prescribed\"). Otherwise null.\n- If \"recognized\" is false, all other fields MUST be null/false/[] -- never guess partial details for something you don't recognize.`;\n\nconst orBody = {\n  model: 'deepseek/deepseek-v4-flash', // cheap, deterministic classification -- temperature 0\n  temperature: 0,\n  messages: [\n    { role: 'system', content: systemPrompt },\n    { role: 'user', content: name }\n  ]\n};\n\nreturn [{ json: { orBody, requestedName: name } }];\n"
      },
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        208,
        208
      ],
      "id": "im0001-0002-4a01-8a01-000000000002",
      "name": "Build Identify Request"
    },
    {
      "parameters": {
        "method": "POST",
        "url": "https://openrouter.ai/api/v1/chat/completions",
        "authentication": "genericCredentialType",
        "genericAuthType": "httpHeaderAuth",
        "sendHeaders": true,
        "headerParameters": {
          "parameters": [
            {
              "name": "Content-Type",
              "value": "application/json"
            }
          ]
        },
        "sendBody": true,
        "specifyBody": "json",
        "jsonBody": "={{ $json.orBody }}",
        "options": {}
      },
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.4,
      "position": [
        416,
        208
      ],
      "id": "im0001-0003-4a01-8a01-000000000003",
      "name": "OpenRouter Identify",
      "credentials": {
        "httpHeaderAuth": {
          "name": "<your credential>"
        }
      }
    },
    {
      "parameters": {
        "jsCode": "// Parse the identification result. Fail CLOSED on any error (never surface a\n// guessed medicine) -- same discipline as the safety-check and scan-photo workflows.\nconst resp = $input.first().json;\nlet content = (resp && resp.choices && resp.choices[0] && resp.choices[0].message && resp.choices[0].message.content) || '';\n\nconst KNOWN_TAGS = new Set(['fever','headache','cough','sore_throat','nausea','allergy','body_ache','muscle_ache','back_ache','sprain','inflammation','runny_nose','blocked_nose','sneezing','phlegm','itchy_eyes','hives','itch','rash','insect_bite','stomach_upset','stomach_cramps','diarrhoea','bloating','gas','indigestion','heartburn','acid_reflux','constipation','dehydration','cuts','scrapes','mouth_ulcer','dry_eyes','eye_irritation','scabies','lice','acne','cold_sore','fungal_infection','dry_skin','eczema','dandruff','warts','insomnia','joint_pain','tiredness','teething','mouth_pain','wound']);\n\nlet parsed;\ntry {\n  content = String(content).replace(/```json/gi, '').replace(/```/g, '').trim();\n  parsed = JSON.parse(content);\n} catch (e) {\n  parsed = { recognized: false };\n}\n\nif (!parsed.recognized) {\n  return [{ json: { recognized: false, canonical_name: null, category: null, symptom_tags: [], long_term: false, general_wellness: false, note: null } }];\n}\n\n// Belt-and-braces: re-filter tags server-side too, don't just trust the model's own compliance.\nconst isSpecialCategory = !!parsed.long_term || !!parsed.general_wellness;\nconst tags = isSpecialCategory ? [] : (Array.isArray(parsed.symptom_tags) ? parsed.symptom_tags.filter(t => KNOWN_TAGS.has(t)) : []);\n\nreturn [{ json: {\n  recognized: true,\n  canonical_name: parsed.canonical_name || null,\n  category: parsed.category || null,\n  symptom_tags: tags,\n  long_term: !!parsed.long_term,\n  general_wellness: !!parsed.general_wellness,\n  note: parsed.note || null\n} }];\n"
      },
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        624,
        208
      ],
      "id": "im0001-0004-4a01-8a01-000000000004",
      "name": "Parse Identification"
    }
  ],
  "connections": {
    "Webhook": {
      "main": [
        [
          {
            "node": "Build Identify Request",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Build Identify Request": {
      "main": [
        [
          {
            "node": "OpenRouter Identify",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "OpenRouter Identify": {
      "main": [
        [
          {
            "node": "Parse Identification",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  },
  "active": false,
  "settings": {
    "executionOrder": "v1"
  },
  "versionId": "cc-identify-medicine-v1",
  "id": "IdentifyMedicine",
  "tags": []
}

Credentials you'll need

Each integration node will prompt for credentials when you import. We strip credential IDs before publishing — you'll add your own.

Pro

For the full experience including quality scoring and batch install features for each workflow upgrade to Pro

About this workflow

Identify Medicine (tier 3 fallback). Uses httpRequest. Webhook trigger; 4 nodes.

Source: https://github.com/Morn-Thuta/CabinetCare_C240_Project/blob/108c6065fbda2497f8f82ad6e6bcb1dd9976e1a0/n8n-workflows/identify-medicine.json — original creator credit. Request a take-down →

More Web Scraping workflows → · Browse all categories →

Related workflows

Workflows that share integrations, category, or trigger type with this one. All free to copy and import.

Web Scraping

This n8n template provides enterprise-level version control for your workflows using GitHub integration. Stop losing hours to broken workflows and manual exports – get proper commit history, visual di

n8n, Execute Workflow Trigger, HTTP Request +1
Web Scraping

This flow creates dummy files for every item added in your *Arrs (Radarr/Sonarr) with the tag .

HTTP Request, Ssh
Web Scraping

eek-Go v2 (Batch-Then-Review). Uses httpRequest. Webhook trigger; 75 nodes.

HTTP Request
Web Scraping

This workflow receives webhook requests from a content calendar and uses the X API v2 to publish text posts, threads, image/video posts, and polls, as well as delete existing posts and run a credentia

HTTP Request
Web Scraping

This workflow acts as a central API gateway for all technical indicator agents in the Binance Spot Market Quant AI system. It listens for incoming webhook requests and dynamically routes them to the c

HTTP Request