AutomationFlowsEmail & Gmail › Calculate Litigation Limitation Deadlines and Reminders in Google Calendar

Calculate Litigation Limitation Deadlines and Reminders in Google Calendar

Calculate litigation limitation deadlines and reminders in Google Calendar. Uses formTrigger, googleSheets, googleCalendar, slack. Event-driven trigger; 18 nodes.

Event trigger★★★★☆ complexity18 nodesForm TriggerGoogle SheetsGoogle CalendarSlackGmail
Email & Gmail Trigger: Event Nodes: 18 Complexity: ★★★★☆ Added:

This workflow follows the Form Trigger → Gmail recipe pattern — see all workflows that pair these two integrations.

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": "Calculate litigation limitation deadlines and reminders in Google Calendar",
  "nodes": [
    {
      "parameters": {
        "path": "5cc975f1-1654-4638-8c48-c348bec55750",
        "formTitle": "Litigation matter intake",
        "formDescription": "Calculate the limitation period and procedural deadlines for a matter. Scheduling aid only, not legal advice.",
        "formFields": {
          "values": [
            {
              "fieldLabel": "Matter name or file number",
              "requiredField": true
            },
            {
              "fieldLabel": "Client name"
            },
            {
              "fieldLabel": "Jurisdiction",
              "fieldType": "dropdown",
              "fieldOptions": {
                "values": [
                  {
                    "option": "Ontario"
                  },
                  {
                    "option": "British Columbia"
                  },
                  {
                    "option": "Alberta"
                  },
                  {
                    "option": "Saskatchewan"
                  },
                  {
                    "option": "Manitoba"
                  },
                  {
                    "option": "Nova Scotia"
                  },
                  {
                    "option": "New Brunswick"
                  },
                  {
                    "option": "Federal"
                  },
                  {
                    "option": "Quebec"
                  }
                ]
              },
              "requiredField": true
            },
            {
              "fieldLabel": "Claim type",
              "fieldType": "dropdown",
              "fieldOptions": {
                "values": [
                  {
                    "option": "General civil"
                  },
                  {
                    "option": "Personal injury / motor vehicle"
                  },
                  {
                    "option": "Contract"
                  },
                  {
                    "option": "Property damage"
                  },
                  {
                    "option": "Defamation (libel/slander)"
                  },
                  {
                    "option": "Municipal (slip and fall)"
                  },
                  {
                    "option": "Professional negligence"
                  }
                ]
              },
              "requiredField": true
            },
            {
              "fieldLabel": "Trigger event type",
              "fieldType": "dropdown",
              "fieldOptions": {
                "values": [
                  {
                    "option": "Date of loss / act or omission"
                  },
                  {
                    "option": "Date of discovery"
                  },
                  {
                    "option": "Date of service of claim"
                  },
                  {
                    "option": "Filing date"
                  }
                ]
              },
              "requiredField": true
            },
            {
              "fieldLabel": "Trigger event date",
              "fieldType": "date",
              "requiredField": true
            },
            {
              "fieldLabel": "Date claim issued or served",
              "fieldType": "date"
            },
            {
              "fieldLabel": "Responsible lawyer email",
              "fieldType": "email"
            },
            {
              "fieldLabel": "Notes",
              "fieldType": "textarea"
            }
          ]
        },
        "options": {}
      },
      "id": "342c6bb9-e26e-4a92-82e2-0f61d2c17eae",
      "name": "Receive Matter Intake Form",
      "type": "n8n-nodes-base.formTrigger",
      "typeVersion": 2,
      "position": [
        576,
        352
      ]
    },
    {
      "parameters": {
        "assignments": {
          "assignments": [
            {
              "id": "77fcd81e-b773-462f-b585-125d4cf68094",
              "name": "limitationRules",
              "value": "{\n  \"Ontario\": {\n    \"General civil\": {\n      \"years\": 2,\n      \"ultimateYears\": 15,\n      \"basis\": \"discovery\",\n      \"citation\": \"Limitations Act, 2002, S.O. 2002, c. 24, Sch. B, ss. 4, 15\",\n      \"note\": \"Basic 2-year discovery period; 15-year ultimate period.\"\n    },\n    \"Personal injury / motor vehicle\": {\n      \"years\": 2,\n      \"ultimateYears\": 15,\n      \"basis\": \"discovery\",\n      \"citation\": \"Limitations Act, 2002, ss. 4, 15\",\n      \"note\": \"Confirm any insurer notice and statutory accident benefit timelines separately.\"\n    },\n    \"Contract\": {\n      \"years\": 2,\n      \"ultimateYears\": 15,\n      \"basis\": \"discovery\",\n      \"citation\": \"Limitations Act, 2002, ss. 4, 15\"\n    },\n    \"Property damage\": {\n      \"years\": 2,\n      \"ultimateYears\": 15,\n      \"basis\": \"discovery\",\n      \"citation\": \"Limitations Act, 2002, ss. 4, 15\"\n    },\n    \"Defamation (libel/slander)\": {\n      \"years\": 2,\n      \"ultimateYears\": 15,\n      \"basis\": \"discovery\",\n      \"citation\": \"Limitations Act, 2002, s. 4; Libel and Slander Act, R.S.O. 1990, c. L.12\",\n      \"note\": \"Newspaper/broadcast: notice within 6 weeks and action within 3 months may apply. Verify.\"\n    },\n    \"Municipal (slip and fall)\": {\n      \"years\": 2,\n      \"ultimateYears\": 15,\n      \"basis\": \"discovery\",\n      \"citation\": \"Limitations Act, 2002, s. 4; Municipal Act, 2001, S.O. 2001, c. 25, s. 44(10)\",\n      \"note\": \"Written notice within 10 days for failure to keep a highway/bridge in repair may apply. Verify.\"\n    },\n    \"Professional negligence\": {\n      \"years\": 2,\n      \"ultimateYears\": 15,\n      \"basis\": \"discovery\",\n      \"citation\": \"Limitations Act, 2002, ss. 4, 5, 15\"\n    }\n  },\n  \"British Columbia\": {\n    \"General civil\": {\n      \"years\": 2,\n      \"ultimateYears\": 15,\n      \"basis\": \"discovery\",\n      \"citation\": \"Limitation Act, S.B.C. 2012, c. 13, ss. 6, 21\"\n    },\n    \"Personal injury / motor vehicle\": {\n      \"years\": 2,\n      \"ultimateYears\": 15,\n      \"basis\": \"discovery\",\n      \"citation\": \"Limitation Act, S.B.C. 2012, ss. 6, 21\"\n    },\n    \"Contract\": {\n      \"years\": 2,\n      \"ultimateYears\": 15,\n      \"basis\": \"discovery\",\n      \"citation\": \"Limitation Act, S.B.C. 2012, ss. 6, 21\"\n    },\n    \"Property damage\": {\n      \"years\": 2,\n      \"ultimateYears\": 15,\n      \"basis\": \"discovery\",\n      \"citation\": \"Limitation Act, S.B.C. 2012, ss. 6, 21\"\n    },\n    \"Professional negligence\": {\n      \"years\": 2,\n      \"ultimateYears\": 15,\n      \"basis\": \"discovery\",\n      \"citation\": \"Limitation Act, S.B.C. 2012, ss. 6, 8, 21\"\n    }\n  },\n  \"Alberta\": {\n    \"General civil\": {\n      \"years\": 2,\n      \"ultimateYears\": 10,\n      \"basis\": \"discovery\",\n      \"citation\": \"Limitations Act, R.S.A. 2000, c. L-12, s. 3\",\n      \"note\": \"Earlier of 2 years from discovery or 10 years from the claim arising.\"\n    },\n    \"Personal injury / motor vehicle\": {\n      \"years\": 2,\n      \"ultimateYears\": 10,\n      \"basis\": \"discovery\",\n      \"citation\": \"Limitations Act, R.S.A. 2000, c. L-12, s. 3\"\n    },\n    \"Contract\": {\n      \"years\": 2,\n      \"ultimateYears\": 10,\n      \"basis\": \"discovery\",\n      \"citation\": \"Limitations Act, R.S.A. 2000, c. L-12, s. 3\"\n    },\n    \"Property damage\": {\n      \"years\": 2,\n      \"ultimateYears\": 10,\n      \"basis\": \"discovery\",\n      \"citation\": \"Limitations Act, R.S.A. 2000, c. L-12, s. 3\"\n    },\n    \"Professional negligence\": {\n      \"years\": 2,\n      \"ultimateYears\": 10,\n      \"basis\": \"discovery\",\n      \"citation\": \"Limitations Act, R.S.A. 2000, c. L-12, s. 3\"\n    }\n  },\n  \"Saskatchewan\": {\n    \"General civil\": {\n      \"years\": 2,\n      \"ultimateYears\": 15,\n      \"basis\": \"discovery\",\n      \"citation\": \"The Limitations Act, S.S. 2004, c. L-16.1, ss. 5, 7\"\n    },\n    \"Contract\": {\n      \"years\": 2,\n      \"ultimateYears\": 15,\n      \"basis\": \"discovery\",\n      \"citation\": \"The Limitations Act, S.S. 2004, c. L-16.1, ss. 5, 7\"\n    },\n    \"Personal injury / motor vehicle\": {\n      \"years\": 2,\n      \"ultimateYears\": 15,\n      \"basis\": \"discovery\",\n      \"citation\": \"The Limitations Act, S.S. 2004, c. L-16.1, ss. 5, 7\"\n    }\n  },\n  \"Manitoba\": {\n    \"General civil\": {\n      \"years\": 2,\n      \"ultimateYears\": 30,\n      \"basis\": \"discovery\",\n      \"citation\": \"The Limitations Act, S.M. 2021, c. 44, ss. 6, 8\",\n      \"note\": \"Basic 2-year discovery period; 30-year ultimate period (in force 2022).\"\n    },\n    \"Contract\": {\n      \"years\": 2,\n      \"ultimateYears\": 30,\n      \"basis\": \"discovery\",\n      \"citation\": \"The Limitations Act, S.M. 2021, c. 44, ss. 6, 8\"\n    },\n    \"Personal injury / motor vehicle\": {\n      \"years\": 2,\n      \"ultimateYears\": 30,\n      \"basis\": \"discovery\",\n      \"citation\": \"The Limitations Act, S.M. 2021, c. 44, ss. 6, 8\"\n    }\n  },\n  \"Nova Scotia\": {\n    \"General civil\": {\n      \"years\": 2,\n      \"ultimateYears\": 15,\n      \"basis\": \"discovery\",\n      \"citation\": \"Limitation of Actions Act, S.N.S. 2014, c. 35, ss. 8, 9\"\n    },\n    \"Contract\": {\n      \"years\": 2,\n      \"ultimateYears\": 15,\n      \"basis\": \"discovery\",\n      \"citation\": \"Limitation of Actions Act, S.N.S. 2014, c. 35, ss. 8, 9\"\n    },\n    \"Personal injury / motor vehicle\": {\n      \"years\": 2,\n      \"ultimateYears\": 15,\n      \"basis\": \"discovery\",\n      \"citation\": \"Limitation of Actions Act, S.N.S. 2014, c. 35, ss. 8, 9\"\n    }\n  },\n  \"New Brunswick\": {\n    \"General civil\": {\n      \"years\": 2,\n      \"ultimateYears\": 15,\n      \"basis\": \"discovery\",\n      \"citation\": \"Limitation of Actions Act, S.N.B. 2009, c. L-8.5, ss. 5, 6\"\n    },\n    \"Contract\": {\n      \"years\": 2,\n      \"ultimateYears\": 15,\n      \"basis\": \"discovery\",\n      \"citation\": \"Limitation of Actions Act, S.N.B. 2009, c. L-8.5, ss. 5, 6\"\n    },\n    \"Personal injury / motor vehicle\": {\n      \"years\": 2,\n      \"ultimateYears\": 15,\n      \"basis\": \"discovery\",\n      \"citation\": \"Limitation of Actions Act, S.N.B. 2009, c. L-8.5, ss. 5, 6\"\n    }\n  },\n  \"Federal\": {\n    \"General civil\": {\n      \"years\": 6,\n      \"ultimateYears\": null,\n      \"basis\": \"act or omission\",\n      \"citation\": \"Crown Liability and Proceedings Act, R.S.C. 1985, c. C-50, s. 32\",\n      \"note\": \"Applies the limitation law of the province where the cause of action arose; 6 years where it did not arise in a province. Verify which applies.\"\n    }\n  },\n  \"Quebec\": {\n    \"General civil\": {\n      \"years\": 3,\n      \"ultimateYears\": null,\n      \"basis\": \"right of action arises\",\n      \"citation\": \"Civil Code of Quebec, art. 2925\",\n      \"note\": \"Quebec uses prescription, not common-law limitation. General movable/personal right: 3 years.\"\n    },\n    \"Personal injury / motor vehicle\": {\n      \"years\": 3,\n      \"ultimateYears\": null,\n      \"basis\": \"right of action arises\",\n      \"citation\": \"Civil Code of Quebec, arts. 2925, 2926\",\n      \"note\": \"Bodily injury: 3 years; date may run from when the injury appears.\"\n    },\n    \"Defamation (libel/slander)\": {\n      \"years\": 1,\n      \"ultimateYears\": null,\n      \"basis\": \"knowledge of the defamation\",\n      \"citation\": \"Civil Code of Quebec, art. 2929\"\n    }\n  }\n}",
              "type": "string"
            },
            {
              "id": "1e88172a-004c-4b1b-8a3e-93a15ed0e0ab",
              "name": "proceduralRules",
              "value": "{\n  \"Ontario\": [\n    {\n      \"name\": \"Statement of defence due (defendant served in Ontario)\",\n      \"fromEvent\": \"service of the claim\",\n      \"days\": 20,\n      \"citation\": \"Rules of Civil Procedure, R.R.O. 1990, Reg. 194, r. 18.01\"\n    }\n  ]\n}",
              "type": "string"
            },
            {
              "id": "a6f91bc9-7177-4b79-8e33-8c4986f9c960",
              "name": "holidays",
              "value": "[\"2026-01-01\",\"2026-02-16\",\"2026-04-03\",\"2026-05-18\",\"2026-07-01\",\"2026-08-03\",\"2026-09-07\",\"2026-09-30\",\"2026-10-12\",\"2026-11-11\",\"2026-12-25\",\"2026-12-28\",\"2027-01-01\",\"2027-02-15\",\"2027-03-26\",\"2027-05-24\",\"2027-07-01\",\"2027-08-02\",\"2027-09-06\",\"2027-09-30\",\"2027-10-11\",\"2027-11-11\",\"2027-12-27\",\"2027-12-28\"]",
              "type": "string"
            },
            {
              "id": "5babc2fe-a8d2-4538-9553-be65a34fe1f6",
              "name": "reminderOffsets",
              "value": "90,30,7",
              "type": "string"
            },
            {
              "id": "a18cab7b-84ac-499e-b110-f98e6db23f64",
              "name": "rollDeadlinesToBusinessDay",
              "value": true,
              "type": "boolean"
            },
            {
              "id": "824de331-1fca-4f84-a230-1292737f63f1",
              "name": "rollRemindersBackToBusinessDay",
              "value": true,
              "type": "boolean"
            },
            {
              "id": "facde12f-8897-4e71-9cec-ee104e549014",
              "name": "timezone",
              "value": "America/Toronto",
              "type": "string"
            },
            {
              "id": "cf422bb8-f90a-4a7c-acab-6f80f59b5314",
              "name": "calendarId",
              "value": "REPLACE_WITH_GOOGLE_CALENDAR_ID",
              "type": "string"
            },
            {
              "id": "5273e40e-d8a3-4a88-9ddb-8072cc89b5e6",
              "name": "logSpreadsheetId",
              "value": "REPLACE_WITH_GOOGLE_SHEET_ID",
              "type": "string"
            },
            {
              "id": "ee284821-da2e-44b2-8f2e-df8a05e842b2",
              "name": "logSheetName",
              "value": "Deadlines",
              "type": "string"
            },
            {
              "id": "462168fb-202c-4afd-9631-c4bd7b1bcb4c",
              "name": "slackChannel",
              "value": "REPLACE_WITH_SLACK_CHANNEL",
              "type": "string"
            },
            {
              "id": "7855a999-95b8-4677-9f50-c484f9b3e3ec",
              "name": "fallbackNotifyEmail",
              "value": "REPLACE_WITH_TEAM_EMAIL",
              "type": "string"
            }
          ]
        },
        "options": {}
      },
      "id": "110471f4-dfa0-468f-a905-0c598b79da02",
      "name": "Set the Rules Table",
      "type": "n8n-nodes-base.set",
      "typeVersion": 3.4,
      "position": [
        848,
        352
      ],
      "notes": "The only place to edit the law. limitationRules, proceduralRules and holidays are JSON; verify every period against the current statute and rules of court before relying on it."
    },
    {
      "parameters": {
        "jsCode": "// Deadline engine (Luxon). Computes the limitation period and any procedural\n// deadlines for one matter, recording the rule applied. DateTime is the Luxon\n// global provided by n8n. Edit the rules in the \"Set the Rules Table\" node, not here.\n\nconst form = $('Receive Matter Intake Form').first().json;\nconst cfg = $('Set the Rules Table').first().json;\n\nfunction asObj(v) { return typeof v === 'string' ? JSON.parse(v) : v; }\nfunction asNums(v) {\n  if (Array.isArray(v)) return v.map(Number);\n  return String(v).split(',').map(s => Number(s.trim())).filter(n => !isNaN(n));\n}\n\nconst limitationRules = asObj(cfg.limitationRules);\nconst proceduralRules = asObj(cfg.proceduralRules);\nconst holidays = asObj(cfg.holidays);\nconst reminderOffsets = asNums(cfg.reminderOffsets);\nconst rollDeadlines = cfg.rollDeadlinesToBusinessDay === true || cfg.rollDeadlinesToBusinessDay === 'true';\nconst rollReminders = cfg.rollRemindersBackToBusinessDay === true || cfg.rollRemindersBackToBusinessDay === 'true';\nconst timezone = cfg.timezone || 'America/Toronto';\n\nconst input = {\n  matterName: form['Matter name or file number'],\n  clientName: form['Client name'] || '',\n  jurisdiction: form['Jurisdiction'],\n  claimType: form['Claim type'],\n  triggerEventType: form['Trigger event type'],\n  triggerEventDate: form['Trigger event date'],\n  serviceDate: form['Date claim issued or served'] || '',\n  responsibleEmail: form['Responsible lawyer email'] || '',\n  notes: form['Notes'] || '',\n};\n\nconst now = DateTime.now().setZone(timezone);\nconst DISCLAIMER = 'Scheduling aid only, not legal advice. Verify against the current statute and rules of court.';\n\n// Anchors a limitation period may validly be measured from. Every rule is discovery-\n// basis; the date of loss/act is a conservative proxy (on or before discovery). A\n// service or filing date falls after discovery, so a limitation measured from it would\n// overstate the time remaining; those anchors drive procedural deadlines only.\nconst LIMITATION_ANCHORS = ['Date of discovery', 'Date of loss / act or omission'];\n\n// Highest year the holiday list covers, to flag deadlines that land beyond it.\nconst maxHolidayYear = holidays.reduce(function (max, d) {\n  const y = Number(String(d).slice(0, 4));\n  return (isFinite(y) && y > max) ? y : max;\n}, 0);\n\nfunction isNonWorkingDay(dt) {\n  if (dt.weekday === 6 || dt.weekday === 7) return true;\n  return holidays.includes(dt.toISODate());\n}\nfunction rollForward(dt) { let d = dt; while (isNonWorkingDay(d)) d = d.plus({ days: 1 }); return d; }\nfunction rollBackward(dt) { let d = dt; while (isNonWorkingDay(d)) d = d.minus({ days: 1 }); return d; }\nfunction buildReminders(deadlineDt) {\n  const out = [];\n  for (const offset of reminderOffsets) {\n    let r = deadlineDt.minus({ days: offset });\n    if (rollReminders) r = rollBackward(r);\n    if (r.startOf('day') < now.startOf('day')) continue;\n    out.push({ label: offset + 'd', date: r.toISODate() });\n  }\n  return out;\n}\n\nconst jur = limitationRules[input.jurisdiction];\nif (!jur) {\n  throw new Error('No limitation rules found for jurisdiction \"' + input.jurisdiction + '\". Add it to the rules table. Available: ' + Object.keys(limitationRules).join(', ') + '.');\n}\nconst rule = jur[input.claimType];\nif (!rule) {\n  throw new Error('No limitation rule for claim type \"' + input.claimType + '\" in ' + input.jurisdiction + '. Add it to the rules table. Available: ' + Object.keys(jur).join(', ') + '.');\n}\n\nconst trigger = DateTime.fromISO(input.triggerEventDate, { zone: timezone });\nif (!trigger.isValid) {\n  throw new Error('Trigger event date \"' + input.triggerEventDate + '\" is not a valid date.');\n}\n\nconst results = [];\n\nfunction makeRecord(deadlineType, deadlineName, basis, fromDateLabel, citation) {\n  return {\n    matterName: input.matterName,\n    clientName: input.clientName,\n    jurisdiction: input.jurisdiction,\n    claimType: input.claimType,\n    deadlineType: deadlineType,\n    deadlineName: deadlineName,\n    ruleCitation: citation,\n    basis: basis,\n    fromDateLabel: fromDateLabel,\n    computedDateRaw: null,\n    computedDate: null,\n    rolled: false,\n    daysUntil: null,\n    reminders: [],\n    warning: '',\n    responsibleEmail: input.responsibleEmail,\n    notes: input.notes,\n    disclaimer: DISCLAIMER,\n  };\n}\n\nfunction pushDeadline(deadlineType, deadlineName, rawDt, basis, fromDateLabel, citation) {\n  const adjusted = rollDeadlines ? rollForward(rawDt) : rawDt;\n  const rec = makeRecord(deadlineType, deadlineName, basis, fromDateLabel, citation);\n  rec.computedDateRaw = rawDt.toISODate();\n  rec.computedDate = adjusted.toISODate();\n  rec.rolled = adjusted.toISODate() !== rawDt.toISODate();\n  rec.daysUntil = Math.round(adjusted.startOf('day').diff(now.startOf('day'), 'days').days);\n  rec.reminders = buildReminders(adjusted);\n  if (rollDeadlines && adjusted.year > maxHolidayYear) {\n    rec.warning = 'Holiday roll covers through ' + maxHolidayYear + '; the ' + adjusted.year + ' date was rolled off weekends only. Extend the holiday list to cover ' + adjusted.year + ' for full accuracy.';\n  }\n  results.push(rec);\n}\n\nfunction pushNote(deadlineType, deadlineName, warning) {\n  const rec = makeRecord(deadlineType, deadlineName, '', 'Trigger event (' + input.triggerEventType + ')', '');\n  rec.warning = warning;\n  results.push(rec);\n}\n\n// Basic limitation period, but only from an anchor it can validly run from. From a\n// service or filing anchor we emit a review note instead of a date that would overstate\n// the time remaining.\nif (LIMITATION_ANCHORS.includes(input.triggerEventType)) {\n  pushDeadline('Limitation', rule.years + '-year limitation period', trigger.plus({ years: rule.years }), rule.basis, 'Trigger event (' + input.triggerEventType + ')', rule.citation);\n} else {\n  pushNote('Limitation (review needed)', 'Limitation not calculated from this trigger type', 'Limitation not calculated: a \"' + input.triggerEventType + '\" date falls after discovery, so a limitation period measured from it would overstate the time remaining. If the action is not yet commenced, re-run using \"Date of discovery\" (or \"Date of loss / act or omission\" as a conservative fallback).');\n}\n\nif (rule.ultimateYears && input.triggerEventType === 'Date of loss / act or omission') {\n  pushDeadline('Ultimate limitation', rule.ultimateYears + '-year ultimate limitation period', trigger.plus({ years: rule.ultimateYears }), 'Act or omission', 'Trigger event (' + input.triggerEventType + ')', rule.citation);\n}\n\nif (input.serviceDate) {\n  const served = DateTime.fromISO(input.serviceDate, { zone: timezone });\n  if (!served.isValid) {\n    throw new Error('Date claim issued/served \"' + input.serviceDate + '\" is not a valid date.');\n  }\n  const procRules = proceduralRules[input.jurisdiction] || [];\n  if (procRules.length === 0) {\n    pushNote('Procedural (none configured)', 'No procedural rule configured for this jurisdiction', 'A claim issued/served date was provided but no procedural rule is configured for ' + input.jurisdiction + '. Verify any defence or response deadline manually, or add a rule to proceduralRules.');\n  } else {\n    for (const p of procRules) {\n      pushDeadline('Procedural', p.name, served.plus({ days: p.days }), p.days + ' days from ' + p.fromEvent, 'Claim issued/served', p.citation);\n    }\n  }\n}\n\nreturn results.map(json => ({ json: json }));\n"
      },
      "id": "fd4da2b0-a747-4998-ac69-de1ac3ce0c85",
      "name": "Calculate Matter Deadlines",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        1200,
        352
      ]
    },
    {
      "parameters": {
        "conditions": {
          "options": {
            "caseSensitive": true,
            "leftValue": "",
            "typeValidation": "loose",
            "version": 2
          },
          "conditions": [
            {
              "id": "a1b2c3d4-0000-4000-8000-000000000001",
              "leftValue": "={{ $json.computedDate }}",
              "rightValue": "",
              "operator": {
                "type": "string",
                "operation": "notEmpty",
                "singleValue": true
              }
            }
          ],
          "combinator": "and"
        },
        "options": {}
      },
      "id": "a1b2c3d4-0000-4000-8000-000000000002",
      "name": "Only Dated Deadlines",
      "type": "n8n-nodes-base.filter",
      "typeVersion": 2.2,
      "position": [
        1488,
        -80
      ]
    },
    {
      "parameters": {
        "operation": "append",
        "documentId": "={{ $('Set the Rules Table').first().json.logSpreadsheetId }}",
        "sheetName": "={{ $('Set the Rules Table').first().json.logSheetName }}",
        "columns": {
          "mappingMode": "defineBelow",
          "value": {
            "Matter": "={{ $json.matterName }}",
            "Client": "={{ $json.clientName }}",
            "Jurisdiction": "={{ $json.jurisdiction }}",
            "Claim type": "={{ $json.claimType }}",
            "Deadline type": "={{ $json.deadlineType }}",
            "Deadline": "={{ $json.deadlineName }}",
            "Due date": "={{ $json.computedDate }}",
            "Statutory date": "={{ $json.computedDateRaw }}",
            "Rolled": "={{ $json.rolled }}",
            "Days until": "={{ $json.daysUntil }}",
            "Rule cited": "={{ $json.ruleCitation }}",
            "Basis": "={{ $json.basis }}",
            "Calculated from": "={{ $json.fromDateLabel }}"
          }
        },
        "options": {
          "cellFormat": "USER_ENTERED"
        }
      },
      "id": "aa9991e4-7b95-4a18-a780-718fba0aa60b",
      "name": "Append Deadline to Matter Log",
      "type": "n8n-nodes-base.googleSheets",
      "typeVersion": 4.4,
      "position": [
        2112,
        352
      ],
      "retryOnFail": true,
      "maxTries": 3,
      "waitBetweenTries": 5000
    },
    {
      "parameters": {
        "resource": "event",
        "operation": "create",
        "start": "={{ $json.computedDate }}",
        "end": "={{ DateTime.fromISO($json.computedDate).plus({ days: 1 }).toISODate() }}",
        "calendar": {
          "__rl": true,
          "mode": "id",
          "value": "={{ $('Set the Rules Table').first().json.calendarId }}"
        },
        "additionalFields": {
          "allday": "yes",
          "summary": "={{ $json.deadlineName }} - {{ $json.matterName }}",
          "description": "=Deadline for {{ $json.matterName }} ({{ $json.jurisdiction }}, {{ $json.claimType }}).\nRule: {{ $json.ruleCitation }}\nBasis: {{ $json.basis }}\nStatutory date: {{ $json.computedDateRaw }}{{ $json.rolled ? \" (moved to next business day)\" : \"\" }}\nResponsible: {{ $json.responsibleEmail }}\n\n{{ $json.disclaimer }}"
        }
      },
      "id": "f6846a4d-efae-49de-a01c-a6abae029c5e",
      "name": "Create Deadline Event in Calendar",
      "type": "n8n-nodes-base.googleCalendar",
      "typeVersion": 1.3,
      "position": [
        1680,
        -80
      ],
      "retryOnFail": true,
      "maxTries": 3,
      "waitBetweenTries": 5000
    },
    {
      "parameters": {
        "fieldToSplitOut": "reminders",
        "include": "allOtherFields",
        "options": {}
      },
      "id": "dccd59ff-8cb1-40fa-bd29-3ee5fa001947",
      "name": "Split Reminders Into Rows",
      "type": "n8n-nodes-base.splitOut",
      "typeVersion": 1,
      "position": [
        1856,
        16
      ]
    },
    {
      "parameters": {
        "resource": "event",
        "operation": "create",
        "start": "={{ $json.reminders.date }}",
        "end": "={{ DateTime.fromISO($json.reminders.date).plus({ days: 1 }).toISODate() }}",
        "calendar": {
          "__rl": true,
          "mode": "id",
          "value": "={{ $('Set the Rules Table').first().json.calendarId }}"
        },
        "additionalFields": {
          "allday": "yes",
          "summary": "=Reminder ({{ $json.reminders.label }}): {{ $json.deadlineName }} - {{ $json.matterName }}",
          "description": "=Upcoming deadline {{ $json.computedDate }} for {{ $json.matterName }} ({{ $json.jurisdiction }}, {{ $json.claimType }}).\nRule: {{ $json.ruleCitation }}\n\n{{ $json.disclaimer }}"
        }
      },
      "id": "b5010587-793d-4888-8d54-1c6674407692",
      "name": "Create Reminder Event in Calendar",
      "type": "n8n-nodes-base.googleCalendar",
      "typeVersion": 1.3,
      "position": [
        2048,
        16
      ],
      "retryOnFail": true,
      "maxTries": 3,
      "waitBetweenTries": 5000
    },
    {
      "parameters": {
        "jsCode": "// Build one human-readable summary across all calculated deadlines for the matter.\n// Dated deadlines show the due date and reminders; review notes (for example a\n// limitation that could not be calculated from the chosen trigger anchor) show their\n// warning, so nothing is dropped silently.\nconst rows = $('Calculate Matter Deadlines').all().map(i => i.json);\nconst cfg = $('Set the Rules Table').first().json;\nif (rows.length === 0) return [];\n\nconst matter = rows[0].matterName;\nconst recipient = rows[0].responsibleEmail || cfg.fallbackNotifyEmail || '';\nconst channel = cfg.slackChannel || '';\n\nconst lines = rows.map(r => {\n  if (!r.computedDate) {\n    // Note row: no calendar date, surface the warning instead.\n    return '- ' + r.deadlineType + ': ' + r.deadlineName + '\\n    ' + r.warning;\n  }\n  const rollNote = r.rolled ? ' (statutory date ' + r.computedDateRaw + ' fell on a weekend or holiday, moved to the next business day)' : '';\n  const rem = r.reminders.length ? r.reminders.map(x => x.label + ' before, on ' + x.date).join('; ') : 'none still upcoming';\n  const warn = r.warning ? '\\n    Note: ' + r.warning : '';\n  return '- ' + r.deadlineType + ': ' + r.deadlineName +\n    '\\n    Due ' + r.computedDate + rollNote + ' (' + r.daysUntil + ' days out)' +\n    '\\n    Rule: ' + r.ruleCitation +\n    '\\n    Reminders: ' + rem + warn;\n});\n\nconst dated = rows.filter(r => r.computedDate).length;\nconst summaryText = 'Calculated deadlines for ' + matter + ' (' + rows[0].jurisdiction + ', ' + rows[0].claimType + ')\\n\\n' +\n  lines.join('\\n\\n') + '\\n\\n' + rows[0].disclaimer;\nconst subject = 'Litigation deadlines: ' + matter;\n\nreturn [{ json: { matterName: matter, recipientEmail: recipient, slackChannel: channel, subject: subject, summaryText: summaryText, deadlineCount: dated, noteCount: rows.length - dated } }];\n"
      },
      "id": "b6aa98ef-6375-46c3-9631-947c030fc332",
      "name": "Build Deadline Summary",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        1504,
        496
      ]
    },
    {
      "parameters": {
        "resource": "message",
        "operation": "post",
        "select": "channel",
        "channelId": {
          "__rl": true,
          "mode": "name",
          "value": "={{ $json.slackChannel }}"
        },
        "messageType": "text",
        "text": "={{ $json.summaryText }}",
        "otherOptions": {}
      },
      "id": "9cd664d0-9eda-4d26-b9df-ec85b2d24c90",
      "name": "Post Deadline Summary to Channel",
      "type": "n8n-nodes-base.slack",
      "typeVersion": 2.2,
      "position": [
        1776,
        496
      ],
      "retryOnFail": true,
      "maxTries": 3,
      "waitBetweenTries": 5000
    },
    {
      "parameters": {
        "resource": "message",
        "operation": "send",
        "sendTo": "={{ $json.recipientEmail }}",
        "subject": "={{ $json.subject }}",
        "message": "={{ $json.summaryText }}",
        "emailType": "text",
        "options": {
          "appendAttribution": false
        }
      },
      "id": "fba7abe5-a401-48f6-bb54-275bd586e58b",
      "name": "Email Deadline Summary to Lawyer",
      "type": "n8n-nodes-base.gmail",
      "typeVersion": 2.1,
      "position": [
        1776,
        672
      ],
      "retryOnFail": true,
      "maxTries": 3,
      "waitBetweenTries": 5000
    },
    {
      "parameters": {
        "content": "## Litigation limitation and deadline calculator\n\nRules-based limitation and procedural deadline calculator for Canadian litigation matters, covering nine jurisdictions. Scheduling aid only, not legal advice.\n\n### How it works\n\n1. An intake form collects the matter, jurisdiction, claim type, trigger event, and trigger date.\n2. An editable rules table holds the multi-province limitation periods, procedural rules, and holidays.\n3. A Luxon engine matches the rule, applies the period, and rolls dates off weekends and holidays.\n4. Each deadline is written to Google Calendar with staggered reminder events and logged to Google Sheets.\n5. A summary of every deadline is posted to Slack and emailed to the responsible lawyer.\n\n### Setup steps\n\n- [ ] Connect Google Calendar, Google Sheets, Gmail, and Slack credentials.\n- [ ] Open Set the Rules Table and set the calendar ID, sheet ID, Slack channel, and team email.\n- [ ] Review the limitation and procedural rules for the jurisdictions you practise in.\n- [ ] Adjust the holiday list and reminder offsets if needed.\n\n### Customization\n\nEdit the rules, holidays, reminder offsets (default 90, 30, 7 days), business-day rolling, and timezone in the one rules node. Add a jurisdiction or claim type by extending the table; the engine picks it up with no code changes.",
        "height": 864,
        "width": 476
      },
      "id": "b8f15b33-3b21-45ac-a6f8-1873563c42ec",
      "name": "Overview",
      "type": "n8n-nodes-base.stickyNote",
      "typeVersion": 1,
      "position": [
        0,
        -80
      ]
    },
    {
      "parameters": {
        "content": "## Collect matter intake\nForm captures the matter, jurisdiction, claim type, and the trigger event and date.",
        "height": 340,
        "width": 232,
        "color": 7
      },
      "id": "5108b36d-e1b0-4ac0-884b-c939971e7828",
      "name": "Group Collect Matter Intake",
      "type": "n8n-nodes-base.stickyNote",
      "typeVersion": 1,
      "position": [
        512,
        176
      ]
    },
    {
      "parameters": {
        "content": "## Set the rules table\nStart here. The only place to edit the law: multi-province limitation rules, procedural rules, holidays, reminder offsets, and the calendar, sheet, Slack, and email placeholders.",
        "height": 388,
        "width": 312,
        "color": 7
      },
      "id": "6c5f3090-e6c3-4244-8f96-80b9225db4bd",
      "name": "Group Set Rules Table",
      "type": "n8n-nodes-base.stickyNote",
      "typeVersion": 1,
      "position": [
        752,
        128
      ]
    },
    {
      "parameters": {
        "content": "## Calculate the deadlines\nLuxon engine matches the rule, applies the period, rolls off weekends and holidays, and cites the rule on every record.",
        "height": 356,
        "width": 360,
        "color": 7
      },
      "id": "3fb543d1-746d-4667-b7fb-7208d8ebec3a",
      "name": "Group Calculate Deadlines",
      "type": "n8n-nodes-base.stickyNote",
      "typeVersion": 1,
      "position": [
        1072,
        160
      ]
    },
    {
      "parameters": {
        "content": "## Write deadlines to calendar\nAll-day event for each deadline, plus one event per staggered reminder.",
        "height": 416,
        "width": 808,
        "color": 7
      },
      "id": "b03cb4e3-8819-44fe-800c-d4c2004daea5",
      "name": "Group Write to Calendar",
      "type": "n8n-nodes-base.stickyNote",
      "typeVersion": 1,
      "position": [
        1440,
        -224
      ]
    },
    {
      "parameters": {
        "content": "## Log to the matter register\nAppends one row per deadline to a Google Sheet register.",
        "height": 328,
        "width": 272,
        "color": 7
      },
      "id": "15a252d5-27f9-40bf-8024-800841ca6d4a",
      "name": "Group Log to Register",
      "type": "n8n-nodes-base.stickyNote",
      "typeVersion": 1,
      "position": [
        2032,
        208
      ]
    },
    {
      "parameters": {
        "content": "## Notify the team\nPosts one Slack message and emails one Gmail summary of all deadlines.",
        "height": 476,
        "width": 584,
        "color": 7
      },
      "id": "3b24c9c1-de98-4030-9adc-da39f8a0d2c5",
      "name": "Group Notify Team",
      "type": "n8n-nodes-base.stickyNote",
      "typeVersion": 1,
      "position": [
        1440,
        368
      ]
    }
  ],
  "connections": {
    "Receive Matter Intake Form": {
      "main": [
        [
          {
            "node": "Set the Rules Table",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Set the Rules Table": {
      "main": [
        [
          {
            "node": "Calculate Matter Deadlines",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Calculate Matter Deadlines": {
      "main": [
        [
          {
            "node": "Append Deadline to Matter Log",
            "type": "main",
            "index": 0
          },
          {
            "node": "Only Dated Deadlines",
            "type": "main",
            "index": 0
          },
          {
            "node": "Split Reminders Into Rows",
            "type": "main",
            "index": 0
          },
          {
            "node": "Build Deadline Summary",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Split Reminders Into Rows": {
      "main": [
        [
          {
            "node": "Create Reminder Event in Calendar",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Build Deadline Summary": {
      "main": [
        [
          {
            "node": "Post Deadline Summary to Channel",
            "type": "main",
            "index": 0
          },
          {
            "node": "Email Deadline Summary to Lawyer",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Only Dated Deadlines": {
      "main": [
        [
          {
            "node": "Create Deadline Event in Calendar",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  },
  "active": false,
  "settings": {
    "executionOrder": "v1"
  },
  "meta": {
    "templateCredsSetupCompleted": false
  },
  "tags": []
}
Pro

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

About this workflow

Calculate litigation limitation deadlines and reminders in Google Calendar. Uses formTrigger, googleSheets, googleCalendar, slack. Event-driven trigger; 18 nodes.

Source: https://github.com/exekyute/n8n-exekyute-templates/blob/main/published/n8n-litigation-limitation-calculator/workflow.json — original creator credit. Request a take-down →

More Email & Gmail workflows → · Browse all categories →

Related workflows

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

Email & Gmail

Automate event registration with capacity management, a waitlist, and multi-tier PDF ticket generation using PDF Generator API. When attendees register, the workflow checks available spots, routes by

Form Trigger, Google Sheets, @Pdfgeneratorapi/N8N Nodes Pdf Generator Api +2
Email & Gmail

Sync your Google Calendar events with Google Sheets and get daily Slack summaries with meeting statistics. FEATURES:

Google Calendar Trigger, Google Sheets, Slack +3
Email & Gmail

Fluxo de Entrevistas. Uses formTrigger, gmail, googleSheets, googleCalendar. Event-driven trigger; 28 nodes.

Form Trigger, Gmail, Google Sheets +1
Email & Gmail

This workflow automatically manages every new Calendly booking from start to finish. When a prospect books a meeting, it captures all details, syncs everything to HubSpot, creates a prep task for the

HTTP Request, Calendly Trigger, ClickUp +4
Email & Gmail

Stop chasing blurry receipts and manually typing expense data. This workflow creates an intelligent, "snap-and-submit" reimbursement pipeline that hosts photos via UploadToURL, extracts deep data via

Form Trigger, N8N Nodes Uploadtourl, HTTP Request +3