Cal.com → n8n → ClickUp: every meeting auto-becomes a task
I take consulting calls. Each one used to leave me with a vaguely remembered note in a Cal.com confirmation email and a follow-up I'd forget to do. Now: every booking lands in a ClickUp "Meetings" list with the time, attendees, notes, and meeting URL — auto-due-at the meeting start time.
Here's how, end-to-end.
The shape of the workflow
Cal.com booking
│ (webhook: BOOKING_CREATED)
▼
n8n workflow
│ (transform payload)
▼
ClickUp create task1. n8n webhook node
In n8n, drop a Webhook trigger node. Set path = calcom-booking, method = POST, response mode = "Using Respond to Webhook node". You'll get a URL like:
https://your-n8n.example.com/webhook/calcom-bookingCopy that URL — you'll paste it into Cal.com in a minute.
2. Cal.com subscription (via API)
Cal.com v2 API. One curl call registers the webhook:
curl -X POST "https://api.cal.com/v2/webhooks" \
-H "Authorization: Bearer $CAL_API_KEY" \
-H "cal-api-version: 2024-08-13" \
-H "Content-Type: application/json" \
-d '{
"subscriberUrl": "https://your-n8n.example.com/webhook/calcom-booking",
"triggers": ["BOOKING_CREATED"],
"active": true
}'Triggers I subscribe to: BOOKING_CREATED only. Reschedules and cancellations also fire, but they double-up the task creation if you let them; better to handle those with separate logic (an update branch, a close branch) which I'll cover another day.
3. ClickUp create-task node
In n8n, add a ClickUp node downstream of the webhook. Operation: Create Task. Pick your list (I made a dedicated "Meetings" list under personal workspace → Team Space).
Field mapping from Cal.com payload to ClickUp task:
| ClickUp field | n8n expression |
|---|---|
| Name | =Meeting: {{ $json.body.payload.title }} |
| Due date | ={{ new Date($json.body.payload.startTime).getTime() }} |
| Description | attendees + notes + video URL (multi-line template) |
| Priority | 2 (high) — these are real commitments |
| Assignees | [your ClickUp user ID] |
The full description template
**Attendees**: {{ payload.attendees.map(a => a.name + ' <' + a.email + '>').join(', ') }}
**Start**: {{ payload.startTime }}
**End**: {{ payload.endTime }}
**Timezone**: {{ payload.organizer.timeZone }}
**Notes**: {{ payload.additionalNotes || '(none)' }}
**Meeting URL**: {{ payload.metadata.videoCallUrl }}
**Booking UID**: {{ payload.uid }}
**Event type**: {{ payload.type }}
_Auto-created from Cal.com via n8n._Test it
Either book a real slot (then cancel right after), or POST a fake payload to your webhook:
curl -X POST -H "Content-Type: application/json" \
-d '{
"triggerEvent": "BOOKING_CREATED",
"payload": {
"title": "Test booking",
"startTime": "2026-05-18T10:00:00Z",
"endTime": "2026-05-18T10:30:00Z",
"uid": "test-001",
"attendees": [{"name":"Test","email":"test@example.com"}],
"organizer": {"name":"Akik","timeZone":"Asia/Dhaka"},
"additionalNotes": "Testing"
}
}' \
https://your-n8n.example.com/webhook/calcom-bookingIf you wired it right, a task appears in your ClickUp list within 5 seconds and the webhook returns:
{"ok":true,"task_id":"86exmkxrt","task_url":"https://app.clickup.com/t/86exmkxrt"}Why this beats Zapier for this case
- Free. n8n self-hosted on the same droplet that already runs everything.
- No execution cap. Zapier free = 100 tasks/mo. n8n self-hosted = unlimited.
- You own the data. Webhook payloads stay on your box.
- Replicable. Export the workflow JSON, version-control it, redeploy anywhere.
The whole thing took 20 minutes including the test cycle. Next workflow on the list: ClickUp comment → Anthropic summary → push back as a comment reply. Stay tuned.
Topics:
Want to Implement These Strategies?
I can help you apply these insights to your business. Book a free consultation today.
Book Your Free Consultation