Issues with Google Calendar Agent – Natural Date Handling & Event ID for Cancel/Reschedule

Hi Community,

I’ve set up an n8n Agent (Gemini 2.5 Flash) that can create, cancel, and reschedule appointments in Google Calendar. Most of it works fine, but I’m facing 2 issues:


1. Natural Date Expressions (Today/Tomorrow)

When a user says a specific date like “17 Sep 2025”, the agent parses it correctly and books the appointment.
But if a user says “today”, “tomorrow”, or “day after tomorrow”, the agent gets confused and sometimes books on the wrong date.

:backhand_index_pointing_right: Question:
How can I force the agent to always interpret natural language dates (today, tomorrow, day after tomorrow) correctly in the user’s timezone (Asia/Karachi)?
Is there a recommended way in n8n to preprocess/translate such relative terms into an absolute date before passing to the agent?


2. Cancel & Reschedule (Event ID Handling)

Cancel and reschedule require the Google Calendar Event ID.

  • The ID is generated only at the time of event creation.

  • The agent shows it in chat when booking is confirmed, but it is not stored in the calendar event summary/title.

  • This makes it impossible to look up later for cancellation or rescheduling.

:backhand_index_pointing_right: Question:
What’s the best practice here?

  • Should I modify the “Create Event” node to append the Event ID into the event summary/title (e.g., Voice Mate Appointment | Ali | Event ID: <id>)?

  • Or is there a better way to persist and later retrieve the Event ID for cancellations and reschedules?


Summary

  • Need a reliable way for the agent to understand natural date expressions.

  • Need a strategy for storing/using Event IDs to support cancel & reschedule.

Has anyone solved a similar issue? Would love some guidance on the cleanest approach.

Thanks!

Here is json flow of n8n “{

“name”: “Voice Mate with Agent”,

“nodes”: [

{

  "parameters": {

    "public": true,

    "options": {}

  },

  "type": "@n8n/n8n-nodes-langchain.chatTrigger",

  "typeVersion": 1.3,

  "position": \[

    -752,

    -48

  \],

  "id": "8f5c8ac0-b45a-4924-868a-8b454adbe5b0",

  "name": "When chat message received",

  "webhookId": "b02ead53-d646-47f6-bf50-98770ed38082"

},

{

  "parameters": {

    "functionCode": "const input = items\[0\] ? items\[0\].json : {};\\nconst now = new Date();\\n// Convert current runtime to UTC, then add +5 hours for Asia/Karachi (PKT)\\nconst utc = new Date(now.getTime() + now.getTimezoneOffset() \* 60000);\\nconst pk = new Date(utc.getTime() + 5 \* 60 \* 60000);\\nconst yyyy = pk.getFullYear();\\nconst mm = String(pk.getMonth() + 1).padStart(2, '0');\\nconst dd = String(pk.getDate()).padStart(2, '0');\\nconst hh = String(pk.getHours()).padStart(2, '0');\\nconst min = String(pk.getMinutes()).padStart(2, '0');\\nconst sec = String(pk.getSeconds()).padStart(2, '0');\\nconst currentDateAsiaKarachi = \`${yyyy}-${mm}-${dd}\`;\\nconst currentDateTimeAsiaKarachi = \`${yyyy}-${mm}-${dd}T${hh}:${min}:${sec}+05:00\`;\\nreturn \[{ json: { ...input, currentDateAsiaKarachi, currentDateTimeAsiaKarachi } }\];"

  },

  "id": "50df11bf-1e29-48bd-91eb-8c4c8afc4b12",

  "name": "inject_current_date",

  "type": "n8n-nodes-base.function",

  "position": \[

    -528,

    -16

  \],

  "typeVersion": 1

},

{

  "parameters": {

    "options": {

      "systemMessage": "=# Agent Name\\nVoice Mate\\n\\n# Tagline\\nThe Voice Mate answers calls, qualifies leads and books jobs so you can focus on the work.  \\nNever miss a call, lead, or sale again.  \\n\\n# Role\\nYou are \*\*Voice Mate\*\*, an AI scheduling assistant that helps users manage appointments via Google Calendar.  \\nAlways be polite, concise, and professional.  \\n\\n# Core Capabilities\\n- Book new appointments  \\n- Show availability for a given date  \\n- Cancel appointments (requires Event ID)  \\n- Reschedule appointments (requires Old Event ID + new time)  \\n- Retrieve existing events  \\n\\n# Booking Strategy\\n- Default appointment length: \*\*1 hour\*\*  \\n- Summary format: \*\*\\"Voice Mate Appointment | {User_Name} | Event ID: {id}\\"\*\*  \\n- \*\* id is event creation id from n8n output json responce\*\*\\n- Always confirm with the user before finalizing booking .\\n\\n# Date Handling Policy\\n- Never calculate or assume the current date/time yourself.\\n- Always use the injected fields:\\n    • currentDateAsiaKarachi → YYYY-MM-DD\\n    • currentDateTimeAsiaKarachi → YYYY-MM-DDTHH:mm:ss+05:00\\n- If the user asks \\"what is today’s date?\\", always reply with currentDateAsiaKarachi.\\n- If the user asks \\"what time is it now?\\", always reply with currentDateTimeAsiaKarachi.- Always use these fields instead of your own clock when asked about the current date/time.\\n- Example: If the user asks \\"what is today’s date?\\", reply with currentDateAsiaKarachi.\\n- Example: If the user asks for a time today, build the ISO string using currentDateAsiaKarachi + user’s requested time.- If the user asks “what is today’s date?”, always reply using \`currentDateAsiaKarachi\`.  \\n  Example: \*\\"Today is {{currentDateAsiaKarachi}} (Asia/Karachi).\\"\*  \\n- Convert times into explicit ISO 8601 date strings (\`YYYY-MM-DD\`) in \*\*Asia/Karachi\*\*.  \\n- \\"Today\\" = \`currentDateAsiaKarachi\`  \\n- \\"Tomorrow\\" = today + 1 day (Asia/Karachi)  \\n- \\"Day after tomorrow\\" = today + 2 days (Asia/Karachi)  \\n- For \*\*Calendar API calls only\*\*, generate \`Start_Time\` and \`End_Time\` in Asia/Karachi.  \\n- Example: If user says \\"today at 3 PM\\", treat \\"3 PM\\" as \*\*Asia/Karachi local time\*\*.  \\n\\n# Availability Search Strategy\\n- User may ask: \\"What times are free on {date}?\\" or \\"Do you have slots around 3pm?\\"  \\n- Always show \*\*all available slots for the requested date\*\*, not just 1 or 2.  \\n- Business hours: \*\*09:00 – 17:00 Asia/Karachi\*\* (exclude 12:00–13:00 for lunch).  \\n- Retrieve free slots from Google Calendar, then present them in \*\*Asia/Karachi format\*\* to the user (e.g., \\"3:00 PM – 4:00 PM\\").  \\n- If no slots are available, reply: \*\\"Sorry, no free slots are available on {date}.\\"\*  \\n\\n# Cancellation Policy\\n- Always require an \*\*Event ID\*\* to cancel an appointment.  \\n- If the user only gives a name/email, explain politely that the Event ID is required.  \\n\\n# Rescheduling Policy\\n- Require the \*\*Old Event ID\*\* to cancel the previous appointment.  \\n- Require a \*\*new valid time\*\* to create the updated event.  \\n\\n# Output Formatting\\n- Be natural and conversational in responses.  \\n- Confirm each action after success (e.g., \\"Your appointment has been booked for 3 PM on Sept 20th\\").  \\n- When showing multiple slots, list them in order:  \\n  Example:  \\n  - 09:00 AM – 10:00 AM  \\n  - 10:00 AM – 11:00 AM  \\n\\n# Goals\\n- Assist users politely and efficiently  \\n- Show availability with \*\*all possible slots\*\* on the requested date  \\n- Always present times and dates in \*\*Asia/Karachi timezone\*\* to the user  \\n- Book only after confirmation  \\n- Log user details (Name, Email, Notes optional)  \\n- Support cancellations and rescheduling  \\n- Send confirmation email after booking  \\n\\n# Tone\\nProfessional, friendly, concise. Always confirm details.  \\n\\n# Guardrails\\n- No personal opinions or confidential info  \\n- Stay focused on scheduling  \\n- Ask clarifying questions if request is vague  \\n\\n# Tools\\n1. \*\*think\*\* → internal reasoning (must run first each turn)  \\n2. \*\*get_availability\*\* → check if a timeslot is open  \\n3. \*\*create_appointment\*\* → create 1-hour booking (after confirmation)  \\n4. \*\*add_event_id\*\* → append Event ID into event summary  \\n5. \*\*update_appointment\*\* → update event with ID in summary  \\n6. \*\*get_events\*\* → list all events (used for cancellation/reschedule lookup)  \\n7. \*\*cancel_appointment\*\* → cancel existing appointment (needs Event ID)  \\n8. \*\*reschedule_appointment\*\* → cancel old + re-book new slot  \\n\\n# Booking Flow\\n1. User requests appointment → run \`get_availability\`  \\n2. Offer all available slots (Asia/Karachi time)  \\n3. After confirmation → collect Name + Email (Notes optional)  \\n4. Call \`create_appointment\`  \\n5. Call \`add_event_id\` → then \`update_appointment\`  \\n6. Confirm verbally + send email  \\n\\n# Cancellation Flow\\n1. Ask user for Name + Email  \\n2. Run \`get_events\` → filter by Name + Email in summary (summary includes \`ID:eventId\`)  \\n3. Extract eventId from summary  \\n4. Call \`cancel_appointment\` with eventId  \\n5. Confirm with user  \\n\\n# Rescheduling Flow\\n1. Ask user for Name + Email  \\n2. Lookup current appointment (via \`get_events\` filtering by summary)  \\n3. Cancel old event with eventId  \\n4. Run availability search for new slot  \\n5. Confirm with user  \\n6. Create new appointment → add eventId → update summary  \\n7. Confirm + send email  \\n"

    }

  },

  "id": "8926210c-fe83-485d-826c-8e2071c344fb",

  "name": "Voice Mate",

  "type": "@n8n/n8n-nodes-langchain.agent",

  "position": \[

    -272,

    -48

  \],

  "typeVersion": 2

},

{

  "parameters": {

    "modelName": "models/gemini-2.5-flash-lite",

    "options": {}

  },

  "id": "f05aac6d-bc16-4c9d-b07a-7127c091ea33",

  "name": "gemini-2.5-flash",

  "type": "@n8n/n8n-nodes-langchain.lmChatGoogleGemini",

  "position": \[

    -720,

    272

  \],

  "typeVersion": 1,

  "credentials": {

    "googlePalmApi": {

      "id": "DHDw98gnRVHdubAa",

      "name": "Google Gemini(PaLM) Api account 2"

    }

  }

},

{

  "parameters": {

    "contextWindowLength": 10

  },

  "id": "f462dff2-33de-4b50-9560-c87f28624800",

  "name": "simple-memory",

  "type": "@n8n/n8n-nodes-langchain.memoryBufferWindow",

  "position": \[

    -576,

    272

  \],

  "typeVersion": 1.3

},

{

  "parameters": {

    "resource": "calendar",

    "calendar": {

      "\__rl": true,

      "value": "[email protected]",

      "mode": "list",

      "cachedResultName": "[email protected]"

    },

    "timeMin": "={{ $fromAI('Start_Time', \`The start timestamp for the appointment.\`, 'string') }}",

    "timeMax": "={{ $fromAI('End_Time', \`The end time will always be the Start Time plus 1 hour.\`, 'string') }}",

    "options": {

      "timezone": {

        "\__rl": true,

        "value": "Asia/Karachi",

        "mode": "list",

        "cachedResultName": "Asia/Karachi"

      }

    }

  },

  "id": "e35aeb71-239c-4276-ad71-ba59cb749fb7",

  "name": "get_availability",

  "type": "n8n-nodes-base.googleCalendarTool",

  "position": \[

    -384,

    272

  \],

  "typeVersion": 1.3,

  "credentials": {

    "googleCalendarOAuth2Api": {

      "id": "LSFObW29k1pPyaLk",

      "name": "Google Calendar account 2"

    }

  }

},

{

  "parameters": {},

  "id": "653db60a-4f6b-43a3-a61f-6e6e386eb49d",

  "name": "think",

  "type": "@n8n/n8n-nodes-langchain.toolThink",

  "position": \[

    -480,

    272

  \],

  "typeVersion": 1

},

{

  "parameters": {

    "calendar": {

      "\__rl": true,

      "value": "[email protected]",

      "mode": "list",

      "cachedResultName": "[email protected]"

    },

    "start": "={{ $fromAI('Start', \`Start time for the appointment (Asia/Karachi). Provide as ISO with +05:00 offset\`, 'string') }}",

    "end": "={{ $fromAI('End', \`End time for the appointment (Asia/Karachi). Provide as ISO with +05:00 offset\`, 'string') }}",

    "additionalFields": {

      "summary": "={{ $fromAI('Summary', \`The title/summary of this event should be in the format \\"Voice Mate Appointment | {user_name}\\" where {user_name} is the provided name of the client.\`, 'string') }} | Event ID: {{ $json\[\\"id\\"\] || '' }}\\n"

    }

  },

  "id": "a4fd91c9-36b3-4d98-a0c5-031883d94701",

  "name": "create_appointment",

  "type": "n8n-nodes-base.googleCalendarTool",

  "position": \[

    -240,

    272

  \],

  "typeVersion": 1.3,

  "credentials": {

    "googleCalendarOAuth2Api": {

      "id": "LSFObW29k1pPyaLk",

      "name": "Google Calendar account 2"

    }

  }

},

{

  "parameters": {

    "operation": "update",

    "calendar": {

      "\__rl": true,

      "value": "[email protected]",

      "mode": "list",

      "cachedResultName": "[email protected]"

    },

    "eventId": "={{ $json\[\\"id\\"\] }}",

    "updateFields": {

      "summary": "={{ \\"Voice Mate Appointment | \\" + $json\[\\"summary\\"\].replace(\\"Appointment | \\", \\"\\") + \\" | Event ID: \\" + $json\[\\"id\\"\] }}"

    }

  },

  "id": "c7cd4fc9-4ae9-4a99-b9e9-816c7d7566ef",

  "name": "update_appointment",

  "type": "n8n-nodes-base.googleCalendarTool",

  "position": \[

    -80,

    272

  \],

  "typeVersion": 1.3,

  "credentials": {

    "googleCalendarOAuth2Api": {

      "id": "LSFObW29k1pPyaLk",

      "name": "Google Calendar account 2"

    }

  }

},

{

  "parameters": {

    "operation": "getAll",

    "calendar": {

      "\__rl": true,

      "value": "[email protected]",

      "mode": "list",

      "cachedResultName": "[email protected]"

    },

    "returnAll": true,

    "options": {}

  },

  "type": "n8n-nodes-base.googleCalendarTool",

  "typeVersion": 1.3,

  "position": \[

    80,

    272

  \],

  "id": "58469b07-7379-46aa-8a41-4c09e15bcbcb",

  "name": "Get many events in Google Calendar",

  "credentials": {

    "googleCalendarOAuth2Api": {

      "id": "LSFObW29k1pPyaLk",

      "name": "Google Calendar account 2"

    }

  }

},

{

  "parameters": {

    "operation": "delete",

    "calendar": {

      "\__rl": true,

      "value": "[email protected]",

      "mode": "list",

      "cachedResultName": "[email protected]"

    },

    "eventId": "={{ $fromAI('Event_ID', \`The ID of the event to cancel.\`, 'string') }}",

    "options": {

      "sendUpdates": "all"

    }

  },

  "id": "0e01266b-45c2-48cc-8d9d-12e3c5ac5108",

  "name": "cancel_appointment",

  "type": "n8n-nodes-base.googleCalendarTool",

  "position": \[

    240,

    272

  \],

  "typeVersion": 1.3,

  "credentials": {

    "googleCalendarOAuth2Api": {

      "id": "LSFObW29k1pPyaLk",

      "name": "Google Calendar account 2"

    }

  }

},

{

  "parameters": {

    "operation": "delete",

    "calendar": {

      "\__rl": true,

      "value": "[email protected]",

      "mode": "list",

      "cachedResultName": "[email protected]"

    },

    "eventId": "={{ $fromAI('Old_Event_ID', \`The ID of the old event to reschedule.\`, 'string') }}",

    "options": {

      "sendUpdates": "all"

    }

  },

  "id": "a12f453f-b6e9-462a-bf7a-570dcca11080",

  "name": "reschedule_appointment",

  "type": "n8n-nodes-base.googleCalendarTool",

  "position": \[

    400,

    272

  \],

  "typeVersion": 1.3,

  "credentials": {

    "googleCalendarOAuth2Api": {

      "id": "LSFObW29k1pPyaLk",

      "name": "Google Calendar account 2"

    }

  }

}

],

“pinData”: {},

“connections”: {

"When chat message received": {

  "main": \[

    \[

      {

        "node": "inject_current_date",

        "type": "main",

        "index": 0

      }

    \]

  \]

},

"inject_current_date": {

  "main": \[

    \[

      {

        "node": "Voice Mate",

        "type": "main",

        "index": 0

      }

    \]

  \]

},

"create_appointment": {

  "ai_tool": \[

    \[

      {

        "node": "Voice Mate",

        "type": "ai_tool",

        "index": 0

      }

    \]

  \]

},

"update_appointment": {

  "ai_tool": \[

    \[\]

  \]

},

"Get many events in Google Calendar": {

  "ai_tool": \[

    \[

      {

        "node": "Voice Mate",

        "type": "ai_tool",

        "index": 0

      }

    \]

  \]

},

"get_availability": {

  "ai_tool": \[

    \[

      {

        "node": "Voice Mate",

        "type": "ai_tool",

        "index": 0

      }

    \]

  \]

},

"cancel_appointment": {

  "ai_tool": \[

    \[

      {

        "node": "Voice Mate",

        "type": "ai_tool",

        "index": 0

      }

    \]

  \]

},

"reschedule_appointment": {

  "ai_tool": \[

    \[

      {

        "node": "Voice Mate",

        "type": "ai_tool",

        "index": 0

      }

    \]

  \]

},

"gemini-2.5-flash": {

  "ai_languageModel": \[

    \[

      {

        "node": "Voice Mate",

        "type": "ai_languageModel",

        "index": 0

      }

    \]

  \]

},

"simple-memory": {

  "ai_memory": \[

    \[

      {

        "node": "Voice Mate",

        "type": "ai_memory",

        "index": 0

      }

    \]

  \]

},

"think": {

  "ai_tool": \[

    \[

      {

        "node": "Voice Mate",

        "type": "ai_tool",

        "index": 0

      }

    \]

  \]

}

},

“active”: true,

“settings”: {

"executionOrder": "v1"

},

“versionId”: “978954f8-8c8f-43ef-a33b-20964e2ca5d6”,

“meta”: {

"templateCredsSetupCompleted": true,

"instanceId": "65c5fc15d6447f2f65ca2c37a350bbcdc9548c187b161d4a993f2270a492eb58"

},

“id”: “Sinnv0gF7pNcCBG8”,

“tags”: [

{

  "name": "training",

  "id": "rBCivbvZajiRnuW1",

  "createdAt": "2025-09-17T11:48:14.581Z",

  "updatedAt": "2025-09-17T11:48:14.581Z"

}

]

}“

Agent Prompt “# Agent Name

Voice Mate

# Tagline

The Voice Mate answers calls, qualifies leads and books jobs so you can focus on the work.

Never miss a call, lead, or sale again.

# Role

You are **Voice Mate**, an AI scheduling assistant that helps users manage appointments via Google Calendar.

Always be polite, concise, and professional.

# Core Capabilities

- Book new appointments

- Show availability for a given date

- Cancel appointments (requires Event ID)

- Reschedule appointments (requires Old Event ID + new time)

- Retrieve existing events

# Booking Strategy

- Default appointment length: **1 hour**

- Summary format: **“Voice Mate Appointment | {User_Name} | Event ID: {id}”**

- ** id is event creation id from n8n output json responce**

- Always confirm with the user before finalizing booking .

# Date Handling Policy

- Never calculate or assume the current date/time yourself.

- Always use the injected fields:

• currentDateAsiaKarachi → YYYY-MM-DD

• currentDateTimeAsiaKarachi → YYYY-MM-DDTHH:mm:ss+05:00

- If the user asks “what is today’s date?”, always reply with currentDateAsiaKarachi.

- If the user asks “what time is it now?”, always reply with currentDateTimeAsiaKarachi.- Always use these fields instead of your own clock when asked about the current date/time.

- Example: If the user asks “what is today’s date?”, reply with currentDateAsiaKarachi.

- Example: If the user asks for a time today, build the ISO string using currentDateAsiaKarachi + user’s requested time.- If the user asks “what is today’s date?”, always reply using `currentDateAsiaKarachi`.

Example: *“Today is {{currentDateAsiaKarachi}} (Asia/Karachi).”*

- Convert times into explicit ISO 8601 date strings (`YYYY-MM-DD`) in **Asia/Karachi**.

- “Today” = `currentDateAsiaKarachi`

- “Tomorrow” = today + 1 day (Asia/Karachi)

- “Day after tomorrow” = today + 2 days (Asia/Karachi)

- For **Calendar API calls only**, generate `Start_Time` and `End_Time` in Asia/Karachi.

- Example: If user says “today at 3 PM”, treat “3 PM” as **Asia/Karachi local time**.

# Availability Search Strategy

- User may ask: “What times are free on {date}?” or “Do you have slots around 3pm?”

- Always show **all available slots for the requested date**, not just 1 or 2.

- Business hours: **09:00 – 17:00 Asia/Karachi** (exclude 12:00–13:00 for lunch).

- Retrieve free slots from Google Calendar, then present them in **Asia/Karachi format** to the user (e.g., “3:00 PM – 4:00 PM”).

- If no slots are available, reply: *“Sorry, no free slots are available on {date}.”*

# Cancellation Policy

- Always require an **Event ID** to cancel an appointment.

- If the user only gives a name/email, explain politely that the Event ID is required.

# Rescheduling Policy

- Require the **Old Event ID** to cancel the previous appointment.

- Require a **new valid time** to create the updated event.

# Output Formatting

- Be natural and conversational in responses.

- Confirm each action after success (e.g., “Your appointment has been booked for 3 PM on Sept 20th”).

- When showing multiple slots, list them in order:

Example:

  • 09:00 AM – 10:00 AM

  • 10:00 AM – 11:00 AM

# Goals

- Assist users politely and efficiently

- Show availability with **all possible slots** on the requested date

- Always present times and dates in **Asia/Karachi timezone** to the user

- Book only after confirmation

- Log user details (Name, Email, Notes optional)

- Support cancellations and rescheduling

- Send confirmation email after booking

# Tone

Professional, friendly, concise. Always confirm details.

# Guardrails

- No personal opinions or confidential info

- Stay focused on scheduling

- Ask clarifying questions if request is vague

# Tools

1. **think** → internal reasoning (must run first each turn)

2. **get_availability** → check if a timeslot is open

3. **create_appointment** → create 1-hour booking (after confirmation)

4. **add_event_id** → append Event ID into event summary

5. **update_appointment** → update event with ID in summary

6. **get_events** → list all events (used for cancellation/reschedule lookup)

7. **cancel_appointment** → cancel existing appointment (needs Event ID)

8. **reschedule_appointment** → cancel old + re-book new slot

# Booking Flow

1. User requests appointment → run `get_availability`

2. Offer all available slots (Asia/Karachi time)

3. After confirmation → collect Name + Email (Notes optional)

4. Call `create_appointment`

5. Call `add_event_id` → then `update_appointment`

6. Confirm verbally + send email

# Cancellation Flow

1. Ask user for Name + Email

2. Run `get_events` → filter by Name + Email in summary (summary includes `ID:eventId`)

3. Extract eventId from summary

4. Call `cancel_appointment` with eventId

5. Confirm with user

# Rescheduling Flow

1. Ask user for Name + Email

2. Lookup current appointment (via `get_events` filtering by summary)

3. Cancel old event with eventId

4. Run availability search for new slot

5. Confirm with user

6. Create new appointment → add eventId → update summary

7. Confirm + send email “

Your LLM agent sometimes mis-parses relative terms, especially across timezones. You’ve already injected currentDateAsiaKarachi and currentDateTimeAsiaKarachi – that’s the right foundation, but the model still tries to “think” dates on its own.

Best Practice Fix is
Pre-process relative dates in a Function node before they ever hit Gemini or Calendar. Don’t rely on the model to calculate.

For example:

const input = items[0].json;
const today = new Date(input.currentDateAsiaKarachi); // already in Asia/Karachi

function format(date) {
  const yyyy = date.getFullYear();
  const mm = String(date.getMonth() + 1).padStart(2, '0');
  const dd = String(date.getDate()).padStart(2, '0');
  return `${yyyy}-${mm}-${dd}`;
}

if (input.userDate === "today") {
  input.absoluteDate = format(today);
} else if (input.userDate === "tomorrow") {
  input.absoluteDate = format(new Date(today.getTime() + 1*24*60*60*1000));
} else if (input.userDate === "day after tomorrow") {
  input.absoluteDate = format(new Date(today.getTime() + 2*24*60*60*1000));
}

return [{ json: input }];

  1. This guarantees that “today” always maps to the Asia/Karachi date you injected.
  2. Pass absoluteDate into the LLM instead of the raw phrase.

Event ID Handling for Cancel & Reschedule

The Problem:
Google Calendar’s Event ID is only generated at creation. If it’s not stored in a retrievable way, your agent can’t find it later.

Best Practice Options:

  1. Append Event ID to the Summary (your idea) - Voice Mate Appointment | Ali | Event ID: 3fhl3k9bci2…

  2. Later, get_events can parse summaries and extract IDs.

  3. Simple, effective, and keeps everything in one place.

  4. Store Event ID in Extended Properties (preferred if you want cleaner titles)

    • Google Calendar supports extendedProperties.private.

    • You can store {"voiceMateId": "<id>"} when creating the event.

    • Later, search/filter using these properties via the API.

    • Cleaner user-facing event titles, more robust data handling.

  5. Maintain an External Mapping in n8n

    • Store {eventId, userName, userEmail} in a separate database (Google Sheet, Supabase, Airtable).

    • Your cancel/reschedule flow first looks up Event ID from this table.

    • More work, but most scalable if you plan multi-user, multi-calendar.

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