Sync project dates and checklists to a spreadsheet
Copy phase dates, milestones and checklist items with people and due dates from Monument into a spreadsheet, and keep dates current with webhooks.
This guide copies project dates and checklists from Monument into a spreadsheet. Your own script then reads the sheet, for example to make Google Tasks. Data goes one way: from Monument to the sheet. Changes in the sheet do not go back to Monument.
The sheet holds three kinds of row: phases with start and end dates, milestones with a target date, and checklist items with a person and a due date.
1. Create an API key
Create a key in Settings under API Keys. The key sees what its creator sees. Projects that person cannot open do not appear. Send the key in the X-API-Key header on every request.
2. List your projects
Keep the id and name of each project you want in the sheet.
curl 'https://api.monument.so/api/v1/projects?limit=100' \
-H 'X-API-Key: monument_your_api_key'3. List each project's phases
Ask for the project's top-level tasks with parentTaskId. If your phases use their own task type, filter by taskTypeId instead. Read the type id from one phase in the first response. Each task has a startDate and an endDate.
# The project's top-level tasks (its phases, in most practices)
curl 'https://api.monument.so/api/v1/tasks?parentTaskId=PROJECT_ID&limit=100' \
-H 'X-API-Key: monument_your_api_key'
# Or every task of one task type in the project
curl 'https://api.monument.so/api/v1/tasks?projectId=PROJECT_ID&taskTypeId=PHASE_TYPE_ID&limit=100' \
-H 'X-API-Key: monument_your_api_key'{
"data": [
{
"id": "TASK_ID",
"name": "Concept design",
"taskTypeId": "PHASE_TYPE_ID",
"startDate": "2026-11-02",
"endDate": "2026-12-18"
}
],
"pagination": { "total": 6, "limit": 100, "offset": 0 }
}4. List each project's milestones
Each milestone has a name and a targetDate.
curl 'https://api.monument.so/api/v1/milestones?projectId=PROJECT_ID&limit=100' \
-H 'X-API-Key: monument_your_api_key'5. List each project's checklist items
Each item has its text, the person it is for (assigneeName), a dueDate, and whether it is done (isCompleted). The taskId tells you which phase or task the item belongs to. Use taskId=TASK_ID instead of projectId to read one task's checklist. This endpoint is read-only.
curl 'https://api.monument.so/api/v1/checklist-items?projectId=PROJECT_ID&limit=100' \
-H 'X-API-Key: monument_your_api_key'{
"data": [
{
"id": "CHECKLIST_ITEM_ID",
"taskId": "TASK_ID",
"projectId": "PROJECT_ID",
"text": "Issue concept drawings to client",
"isCompleted": false,
"completedAt": null,
"assigneeResourceId": "RESOURCE_ID",
"assigneeName": "Sam Taylor",
"dueDate": "2026-11-14",
"displayOrder": 0
}
],
"pagination": { "total": 12, "limit": 100, "offset": 0 }
}pagination.total is larger than the rows you got, ask again with offset=100, then offset=200, and so on.6. Get told when dates change
A webhook sends a message to your server when a date moves, so you do not have to read every project again. Subscribe to task.dates_changed and milestone.dates_changed. Only an admin key can create webhooks. The response has a signing secret that starts with whsec_. Store it.
curl -X POST 'https://api.monument.so/api/v1/webhooks' \
-H 'X-API-Key: monument_your_admin_api_key' \
-H 'Content-Type: application/json' \
-d '{
"name": "Project dates to spreadsheet",
"url": "https://example.com/hooks/monument",
"events": ["task.dates_changed", "milestone.dates_changed"]
}'Monument sends one message per task or milestone whose dates changed. A drag that moves five phases sends five messages. Renaming or other edits send nothing. Each message gives the dates before and after the change:
{
"event": "task.dates_changed",
"timestamp": "2026-10-28T09:15:00.000Z",
"data": {
"id": "TASK_ID",
"projectId": "PROJECT_ID",
"rootTaskId": "PROJECT_ID",
"name": "Concept design",
"previous": { "startDate": "2026-11-02", "endDate": "2026-12-18" },
"current": { "startDate": "2026-11-09", "endDate": "2026-12-23" }
}
}{
"event": "milestone.dates_changed",
"timestamp": "2026-10-28T09:15:00.000Z",
"data": {
"id": "MILESTONE_ID",
"projectId": "PROJECT_ID",
"rootTaskId": "PROJECT_ID",
"name": "Planning submission",
"previous": { "targetDate": "2027-01-15" },
"current": { "targetDate": "2027-01-29" }
}
}7. Check the signature
Each message has an X-Monument-Signature header. It is sha256= followed by an HMAC-SHA256 of the raw body, made with your webhook secret. Make the same value on your side and compare. If they differ, ignore the message.
import { createHmac, timingSafeEqual } from 'node:crypto';
// rawBody: the request body exactly as received, before JSON.parse
function isValidSignature(rawBody, signatureHeader, secret) {
const expected = createHmac('sha256', secret).update(rawBody).digest('hex');
const received = signatureHeader.replace('sha256=', '');
const a = Buffer.from(expected);
const b = Buffer.from(received);
return a.length === b.length && timingSafeEqual(a, b);
}8. Map the data to sheet rows
Give the sheet one row per phase, milestone and checklist item. Keep the Monument id in a column, so an update finds its row again.
- Phase: Type
phase, Id fromid, Project, Name, Start fromstartDate, End fromendDate. - Milestone: Type
milestone, Id, Project, Name, Due fromtargetDate. - Checklist item: Type
checklist, Id, Project, Name fromtext, Due fromdueDate, Person fromassigneeName, Done fromisCompleted. - On
task.dates_changed: find the row withdata.idand writedata.current.startDateanddata.current.endDate. - On
milestone.dates_changed: find the row withdata.idand writedata.current.targetDate.
Checklist changes do not send webhooks. Read the checklist items again on a timer, for example every hour, to pick up new items, new people and new due dates.