{
  "name": "Nodbot - amoCRM webhook deduplication with Postgres",
  "nodes": [
    {
      "parameters": {
        "httpMethod": "POST",
        "path": "amocrm-webhook-deduplication",
        "responseMode": "responseNode"
      },
      "id": "amocrm-webhook-deduplication-01",
      "name": "Webhook input",
      "type": "n8n-nodes-base.webhook",
      "typeVersion": 2,
      "position": [
        -720,
        40
      ]
    },
    {
      "parameters": {
        "jsCode": "const event = $json.body ?? $json;\nconst account = event.account?.id ?? event.account_id ?? 'unknown-account';\nconst entityId = event.leads?.update?.[0]?.id ?? event.leads?.add?.[0]?.id ?? event.entity_id;\nconst updatedAt = event.leads?.update?.[0]?.updated_at ?? event.updated_at ?? event.timestamp ?? '';\nif (!entityId) throw new Error('Cannot build amoCRM webhook idempotency key without entity id');\nreturn [{ json: {\n  idempotency_key: `amocrm:${account}:lead:${entityId}:${updatedAt}`,\n  account_id: account,\n  entity_type: 'lead',\n  entity_id: String(entityId),\n  event_time: updatedAt,\n  raw_event_type: event.leads?.add ? 'lead_add' : event.leads?.update ? 'lead_update' : 'unknown'\n}}];"
      },
      "id": "amocrm-webhook-deduplication-02",
      "name": "Build idempotency key",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        -440,
        40
      ]
    },
    {
      "parameters": {
        "operation": "executeQuery",
        "query": "INSERT INTO idempotency_keys(key, source, entity_type, entity_id) VALUES($1, $2, $3, $4) ON CONFLICT DO NOTHING RETURNING key;"
      },
      "id": "amocrm-webhook-deduplication-03",
      "name": "Insert idempotency key",
      "type": "n8n-nodes-base.postgres",
      "typeVersion": 2.4,
      "position": [
        -160,
        40
      ]
    },
    {
      "parameters": {
        "method": "POST",
        "url": "https://YOUR_INTERNAL_ENDPOINT/process-amocrm-event"
      },
      "id": "amocrm-webhook-deduplication-04",
      "name": "Process business action once",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.2,
      "position": [
        120,
        40
      ]
    },
    {
      "parameters": {
        "respondWith": "json",
        "responseBody": "={{ { ok: true, status: \"accepted\" } }}",
        "options": {
          "responseCode": 200
        }
      },
      "id": "amocrm-webhook-deduplication-05",
      "name": "Respond to Webhook",
      "type": "n8n-nodes-base.respondToWebhook",
      "typeVersion": 1.1,
      "position": [
        400,
        40
      ]
    }
  ],
  "connections": {
    "Webhook input": {
      "main": [
        [
          {
            "node": "Build idempotency key",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Build idempotency key": {
      "main": [
        [
          {
            "node": "Insert idempotency key",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Insert idempotency key": {
      "main": [
        [
          {
            "node": "Process business action once",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Process business action once": {
      "main": [
        [
          {
            "node": "Respond to Webhook",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  },
  "pinData": {},
  "settings": {
    "executionOrder": "v1"
  },
  "staticData": null,
  "tags": [
    {
      "name": "nodbot"
    },
    {
      "name": "amocrm"
    },
    {
      "name": "webhook"
    },
    {
      "name": "idempotency"
    },
    {
      "name": "postgres"
    }
  ],
  "triggerCount": 0,
  "updatedAt": "2026-05-30T00:00:00.000Z",
  "versionId": "nodbot-amocrm-webhook-deduplication-problem-solution-v1",
  "meta": {
    "templateCredsSetupCompleted": false
  },
  "notes": "Problem/Solution-мануал для amoCRM webhooks в n8n: как построить idempotency key, записать его в Postgres с unique constraint, обработать событие один раз и безопасно отвечать на повторы. Replace credentials, endpoints and custom field IDs before production."
}