AutomationFlowsData & Sheets › Webhook to Postgres Data Import

Webhook to Postgres Data Import

Original n8n title: Sistema - Importar Datos

Sistema - Importar Datos. Uses postgres. Webhook trigger; 15 nodes.

Webhook trigger★★★★☆ complexity15 nodesPostgres
Data & Sheets Trigger: Webhook Nodes: 15 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": "Sistema - Importar Datos",
  "nodes": [
    {
      "id": "node-webhook",
      "name": "Webhook",
      "type": "n8n-nodes-base.webhook",
      "typeVersion": 2,
      "position": [
        100,
        300
      ],
      "parameters": {
        "httpMethod": "POST",
        "path": "system/data-import",
        "responseMode": "responseNode",
        "options": {}
      }
    },
    {
      "id": "node-prepare",
      "name": "Preparar Registros",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        320,
        300
      ],
      "parameters": {
        "jsCode": "// Extrae entity + records del body\nconst body = $input.first().json.body || $input.first().json;\nconst entity = String(body.entity || '').toLowerCase().trim();\nconst records = Array.isArray(body.records) ? body.records : [];\n\nif (records.length === 0) {\n  throw new Error('No se recibieron registros para importar');\n}\n\n// Agrega metadatos de tracking a cada registro\nreturn records.map((record, index) => ({\n  json: {\n    ...record,\n    _index: index + 1,\n    _entity: entity,\n    _status: 'pending',\n    _error: null,\n  }\n}));"
      }
    },
    {
      "id": "node-loop",
      "name": "Loop por Registro",
      "type": "n8n-nodes-base.splitInBatches",
      "typeVersion": 3,
      "position": [
        540,
        300
      ],
      "parameters": {
        "batchSize": 1,
        "options": {}
      }
    },
    {
      "id": "node-switch",
      "name": "Switch: Entidad",
      "type": "n8n-nodes-base.switch",
      "typeVersion": 3,
      "position": [
        760,
        300
      ],
      "parameters": {
        "mode": "rules",
        "rules": {
          "values": [
            {
              "conditions": {
                "options": {
                  "caseSensitive": false,
                  "leftValue": "",
                  "typeValidation": "strict"
                },
                "combinator": "and",
                "conditions": [
                  {
                    "leftValue": "={{ $json._entity }}",
                    "rightValue": "patients",
                    "operator": {
                      "type": "string",
                      "operation": "equals"
                    }
                  }
                ]
              },
              "renameOutput": true,
              "outputKey": "Pacientes"
            }
          ]
        },
        "fallbackOutput": "extra",
        "looseTypeValidation": true
      }
    },
    {
      "id": "node-pg-select",
      "name": "Buscar Paciente",
      "type": "n8n-nodes-base.postgres",
      "typeVersion": 2.5,
      "position": [
        980,
        200
      ],
      "parameters": {
        "operation": "executeQuery",
        "query": "SELECT id FROM users WHERE is_sales = true AND (\n  (NULLIF($1, '') IS NOT NULL AND email = $1)\n  OR (NULLIF($2, '') IS NOT NULL AND phone_number = $2)\n  OR (NULLIF($3, '') IS NOT NULL AND identity_document = $3)\n  OR (NULLIF($4, '') IS NOT NULL AND internal_id = $4)\n) LIMIT 1",
        "additionalFields": {
          "queryParams": "={{ [\n  $json.email ?? '',\n  $json.phone_number ?? '',\n  $json.identity_document ?? '',\n  $json.internal_id ?? ''\n] }}"
        }
      },
      "credentials": {
        "postgres": {
          "name": "<your credential>"
        }
      },
      "continueOnFail": true,
      "alwaysOutputData": true
    },
    {
      "id": "node-if-exists",
      "name": "\u00bfPaciente Existe?",
      "type": "n8n-nodes-base.if",
      "typeVersion": 2,
      "position": [
        1200,
        200
      ],
      "parameters": {
        "conditions": {
          "options": {
            "caseSensitive": true,
            "leftValue": "",
            "typeValidation": "strict"
          },
          "combinator": "and",
          "conditions": [
            {
              "leftValue": "={{ ($json.id ?? '').toString() }}",
              "rightValue": "",
              "operator": {
                "type": "string",
                "operation": "notEmpty",
                "singleValue": true
              }
            }
          ]
        }
      }
    },
    {
      "id": "node-prep-update",
      "name": "Preparar Actualizaci\u00f3n",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        1420,
        100
      ],
      "parameters": {
        "jsCode": "// Combina el id encontrado (SELECT result) con los datos originales del loop\nconst found = $input.first().json;\nconst original = $('Loop por Registro').item.json;\n\nreturn [{\n  json: {\n    _found_id:         found.id,\n    _index:            original._index,\n    _entity:           original._entity,\n    _name:             original.name || '',\n    name:              original.name              ?? '',\n    email:             original.email             ?? '',\n    phone_number:      original.phone_number      ?? '',\n    identity_document: original.identity_document ?? '',\n    internal_id:       original.internal_id       ?? '',\n    birthday:          original.birthday          ?? '',\n    address:           original.address           ?? '',\n    alternative_phone: original.alternative_phone ?? '',\n    notes:             original.notes             ?? '',\n    rut:               original.rut               ?? '',\n    bank_account:      original.bank_account      ?? '',\n    is_active:         original.is_active !== undefined ? String(original.is_active) : '',\n  }\n}];"
      }
    },
    {
      "id": "node-pg-update",
      "name": "Actualizar Paciente",
      "type": "n8n-nodes-base.postgres",
      "typeVersion": 2.5,
      "position": [
        1640,
        100
      ],
      "parameters": {
        "operation": "executeQuery",
        "query": "UPDATE users SET\n  name              = CASE WHEN $2  <> '' THEN $2                    ELSE name              END,\n  email             = CASE WHEN $3  <> '' THEN $3                    ELSE email             END,\n  phone_number      = CASE WHEN $4  <> '' THEN $4                    ELSE phone_number      END,\n  identity_document = CASE WHEN $5  <> '' THEN $5                    ELSE identity_document END,\n  internal_id       = CASE WHEN $6  <> '' THEN $6                    ELSE internal_id       END,\n  birthday          = CASE WHEN $7  <> '' THEN $7::date              ELSE birthday          END,\n  address           = CASE WHEN $8  <> '' THEN $8                    ELSE address           END,\n  alternative_phone = CASE WHEN $9  <> '' THEN $9                    ELSE alternative_phone END,\n  notes             = CASE WHEN $10 <> '' THEN $10                   ELSE notes             END,\n  rut               = CASE WHEN $11 <> '' THEN $11                   ELSE rut               END,\n  bank_account      = CASE WHEN $12 <> '' THEN $12                   ELSE bank_account      END,\n  is_active         = CASE WHEN $13 <> '' THEN $13::boolean          ELSE is_active         END,\n  updated_at        = CURRENT_TIMESTAMP\nWHERE id = $1::uuid\nRETURNING id",
        "additionalFields": {
          "queryParams": "={{ [\n  $json._found_id,\n  $json.name,\n  $json.email,\n  $json.phone_number,\n  $json.identity_document,\n  $json.internal_id,\n  $json.birthday,\n  $json.address,\n  $json.alternative_phone,\n  $json.notes,\n  $json.rut,\n  $json.bank_account,\n  $json.is_active\n] }}"
        }
      },
      "credentials": {
        "postgres": {
          "name": "<your credential>"
        }
      },
      "continueOnFail": true
    },
    {
      "id": "node-tag-updated",
      "name": "Marcar Actualizado",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        1860,
        100
      ],
      "parameters": {
        "jsCode": "const prep   = $('Preparar Actualizaci\u00f3n').item.json;\nconst result = $input.first().json;\n\nif (result.error || !result.id) {\n  const msg = result.error?.message || String(result.error) || 'Error al actualizar registro';\n  return [{ json: { _index: prep._index, _entity: prep._entity, _status: 'error', _error: msg, _name: prep._name } }];\n}\n\nreturn [{ json: { _index: prep._index, _entity: prep._entity, _status: 'updated', _id: result.id, _name: prep._name } }];"
      }
    },
    {
      "id": "node-prep-insert",
      "name": "Preparar Inserci\u00f3n",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        1420,
        320
      ],
      "parameters": {
        "jsCode": "// Recupera los datos originales del loop para preparar el INSERT\nconst original = $('Loop por Registro').item.json;\n\nreturn [{\n  json: {\n    _index:            original._index,\n    _entity:           original._entity,\n    _name:             original.name || '',\n    name:              original.name              ?? '',\n    email:             original.email             ?? '',\n    phone_number:      original.phone_number      ?? '',\n    identity_document: original.identity_document ?? '',\n    internal_id:       original.internal_id       ?? '',\n    birthday:          original.birthday          ?? '',\n    address:           original.address           ?? '',\n    alternative_phone: original.alternative_phone ?? '',\n    notes:             original.notes             ?? '',\n    rut:               original.rut               ?? '',\n    bank_account:      original.bank_account      ?? '',\n    is_active:         original.is_active !== undefined ? String(original.is_active) : '',\n  }\n}];"
      }
    },
    {
      "id": "node-pg-insert",
      "name": "Insertar Paciente",
      "type": "n8n-nodes-base.postgres",
      "typeVersion": 2.5,
      "position": [
        1640,
        320
      ],
      "parameters": {
        "operation": "executeQuery",
        "query": "INSERT INTO users (\n  name, email, phone_number, identity_document, internal_id,\n  birthday, address, alternative_phone, notes, rut, bank_account,\n  is_active, is_sales\n) VALUES (\n  $1,\n  NULLIF($2,  ''),\n  NULLIF($3,  ''),\n  NULLIF($4,  ''),\n  NULLIF($5,  ''),\n  NULLIF($6,  '')::date,\n  NULLIF($7,  ''),\n  NULLIF($8,  ''),\n  NULLIF($9,  ''),\n  NULLIF($10, ''),\n  NULLIF($11, ''),\n  COALESCE(NULLIF($12, '')::boolean, true),\n  true\n)\nRETURNING id",
        "additionalFields": {
          "queryParams": "={{ [\n  $json.name,\n  $json.email,\n  $json.phone_number,\n  $json.identity_document,\n  $json.internal_id,\n  $json.birthday,\n  $json.address,\n  $json.alternative_phone,\n  $json.notes,\n  $json.rut,\n  $json.bank_account,\n  $json.is_active\n] }}"
        }
      },
      "credentials": {
        "postgres": {
          "name": "<your credential>"
        }
      },
      "continueOnFail": true
    },
    {
      "id": "node-tag-inserted",
      "name": "Marcar Insertado",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        1860,
        320
      ],
      "parameters": {
        "jsCode": "const prep   = $('Preparar Inserci\u00f3n').item.json;\nconst result = $input.first().json;\n\nif (result.error || !result.id) {\n  const msg = result.error?.message || String(result.error) || 'Error al insertar registro';\n  return [{ json: { _index: prep._index, _entity: prep._entity, _status: 'error', _error: msg, _name: prep._name } }];\n}\n\nreturn [{ json: { _index: prep._index, _entity: prep._entity, _status: 'inserted', _id: result.id, _name: prep._name } }];"
      }
    },
    {
      "id": "node-unsupported",
      "name": "Entidad No Soportada",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        980,
        420
      ],
      "parameters": {
        "jsCode": "const item = $input.first().json;\nreturn [{ json: {\n  _index:  item._index,\n  _entity: item._entity,\n  _status: 'skipped',\n  _error:  `Entidad '${item._entity}' no soportada en esta versi\u00f3n`,\n  _name:   item.name || ''\n} }];"
      }
    },
    {
      "id": "node-aggregate",
      "name": "Agregar Resultados",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        760,
        520
      ],
      "parameters": {
        "jsCode": "// Recibe todos los items procesados del loop y genera el resumen\nconst items = $input.all();\n\nconst inserted = items.filter(i => i.json._status === 'inserted').length;\nconst updated  = items.filter(i => i.json._status === 'updated').length;\nconst skipped  = items.filter(i => i.json._status === 'skipped').length;\nconst errors   = items.filter(i => i.json._status === 'error').length;\nconst total    = items.length;\n\nconst error_details = items\n  .filter(i => i.json._status === 'error')\n  .map(i => ({\n    row:   i.json._index,\n    name:  i.json._name  || '',\n    error: i.json._error || 'Error desconocido',\n  }));\n\nreturn [{ json: { total, inserted, updated, skipped, errors, error_details } }];"
      }
    },
    {
      "id": "node-respond",
      "name": "Responder",
      "type": "n8n-nodes-base.respondToWebhook",
      "typeVersion": 1.1,
      "position": [
        980,
        520
      ],
      "parameters": {
        "respondWith": "json",
        "responseBody": "={{ $json }}",
        "options": {
          "responseCode": 200
        }
      }
    }
  ],
  "connections": {
    "Webhook": {
      "main": [
        [
          {
            "node": "Preparar Registros",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Preparar Registros": {
      "main": [
        [
          {
            "node": "Loop por Registro",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Loop por Registro": {
      "main": [
        [
          {
            "node": "Switch: Entidad",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Agregar Resultados",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Switch: Entidad": {
      "main": [
        [
          {
            "node": "Buscar Paciente",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Entidad No Soportada",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Buscar Paciente": {
      "main": [
        [
          {
            "node": "\u00bfPaciente Existe?",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "\u00bfPaciente Existe?": {
      "main": [
        [
          {
            "node": "Preparar Actualizaci\u00f3n",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Preparar Inserci\u00f3n",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Preparar Actualizaci\u00f3n": {
      "main": [
        [
          {
            "node": "Actualizar Paciente",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Actualizar Paciente": {
      "main": [
        [
          {
            "node": "Marcar Actualizado",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Marcar Actualizado": {
      "main": [
        [
          {
            "node": "Loop por Registro",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Preparar Inserci\u00f3n": {
      "main": [
        [
          {
            "node": "Insertar Paciente",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Insertar Paciente": {
      "main": [
        [
          {
            "node": "Marcar Insertado",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Marcar Insertado": {
      "main": [
        [
          {
            "node": "Loop por Registro",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Entidad No Soportada": {
      "main": [
        [
          {
            "node": "Loop por Registro",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Agregar Resultados": {
      "main": [
        [
          {
            "node": "Responder",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  },
  "active": false,
  "settings": {
    "executionOrder": "v1"
  },
  "meta": {
    "templateCredsSetupCompleted": false,
    "notes": "CONFIGURACI\u00d3N REQUERIDA:\n1. Crear una credencial de tipo 'Postgres' en n8n con los datos de conexi\u00f3n a la DB.\n2. En los nodos 'Buscar Paciente', 'Actualizar Paciente' e 'Insertar Paciente', seleccionar esa credencial (reemplaza POSTGRES_CREDENTIAL_ID).\n3. Activar el workflow.\n\nRUTA DEL WEBHOOK:\nPOST https://<tu-n8n>/webhook/system/data-import\n\nPAYLOAD ESPERADO:\n{ \"entity\": \"patients\", \"records\": [ { \"name\": \"...\", \"email\": \"...\" } ] }\n\nENTIDADES SOPORTADAS ACTUALMENTE:\n- patients (tabla public.users con is_sales=true)\n- Otras entidades retornan _status='skipped'\n\nRESPUESTA:\n{ \"total\": N, \"inserted\": N, \"updated\": N, \"skipped\": N, \"errors\": N, \"error_details\": [...] }"
  }
}

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

Sistema - Importar Datos. Uses postgres. Webhook trigger; 15 nodes.

Source: https://github.com/markinvokeia/studio/blob/180b2144592ff19ba4f3bb9a86b84975adeac811/n8n-workflows/import-data.json — original creator credit. Request a take-down →

More Data & Sheets workflows → · Browse all categories →

Related workflows

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

Data & Sheets

Scraping. Uses httpRequest, postgres, @apify/n8n-nodes-apify, respondToWebhook. Webhook trigger; 61 nodes.

HTTP Request, Postgres, @Apify/N8N Nodes Apify
Data & Sheets

Workflow B — AI Listing Engine. Uses httpRequest, postgres, errorTrigger. Webhook trigger; 47 nodes.

HTTP Request, Postgres, Error Trigger
Data & Sheets

How it works

Postgres, Email Send
Data & Sheets

This workflow automates data maturity evaluation to measure how well an organization uses data to create value by capturing assessment data through forms or APIs, processing and scoring responses usin

Email Send, Postgres
Data & Sheets

Orders. Uses postgres. Webhook trigger; 26 nodes.

Postgres