Track project progress with the activity heatmap, due dates in frontmatter, overdue alerts, and external calendar sync.
Lifestream Vault turns your Markdown documents into a living project tracker. Every document can carry a due date in its frontmatter, and the calendar system surfaces those dates in an agenda view, sends overdue alerts, and exports your schedule as an iCal feed for your preferred calendar app.
By the end of this guide you will:
due dates to documents using YAML frontmatterPrerequisites
/due and /agenda endpoints are gated by the calendarDueDates feature flag)calendarIcalExport feature flag is Business-only)The activity heatmap is a GitHub-style contribution graph displayed on your vault's calendar page. Each cell represents one day; its intensity reflects the number of document events (creates, updates, deletes) that occurred on that day.
What it measures:
What it does not measure:
How to read it: The heatmap rolls up the past 52 weeks, so you can see your writing cadence at a glance. A consistently active heatmap indicates a healthy knowledge-management habit. Cold streaks (light cells) are useful signals that a project is stalling.
In the UI: open any vault and click Calendar in the left navigation. The heatmap appears at the top of the page. Hover over any cell to see the exact count of events for that day. Click a cell to jump to a filtered document list for that date.
Via the API: the heatmap data is available at GET /api/v1/vaults/:vaultId/calendar/activity and returns a date-keyed object of daily event counts covering the past 365 days.
The simplest way to add a due date is to include a due field in the document's YAML frontmatter. Use ISO 8601 format (YYYY-MM-DD) — the calendar system parses this field on every write and indexes it for fast querying.
---
title: Launch SDK v2
tags: [sdk, launch, q1-2026]
due: 2026-03-31
---
# Launch SDK v2
## Checklist
- [ ] Finalize breaking changes
- [ ] Update changelog
- [ ] Publish to npm
- [ ] Notify integrators
The due field works alongside any other frontmatter fields you use. There is no schema enforcement — just make sure the value is a valid ISO date string.
import { LifestreamVaultClient } from '@lifestreamdynamics/vault-sdk';
const { client } = await LifestreamVaultClient.login(
'https://vault.lifestreamdynamics.com',
process.env.VAULT_EMAIL!,
process.env.VAULT_PASSWORD!,
);
const VAULT_ID = 'vault-uuid';
// Create a document with a due date in frontmatter
const doc = await client.documents.put(
VAULT_ID,
'projects/launch-sdk-v2.md',
`---
title: Launch SDK v2
tags: [sdk, launch, q1-2026]
due: 2026-03-31
---
# Launch SDK v2
## Checklist
- [ ] Finalize breaking changes
- [ ] Update changelog
- [ ] Publish to npm
- [ ] Notify integrators
`,
);
console.log('Document created:', doc.id);
// The 'due' field is parsed from frontmatter and indexed server-side.
// Query it back via client.calendar.getDueDates(VAULT_ID) — it is not
// returned on the Document object itself.Due dates are extracted from frontmatter at write time and stored as a dedicated indexed column in the database. This means filtering by due is fast regardless of vault size. You do not need to re-index existing documents — the backfill-search admin CLI command (npm run backfill-search -w packages/api) also rebuilds due date indexes.
The due-date-check.worker runs on a 15-minute cron schedule and identifies documents whose due date has passed. Overdue documents are flagged in the database and can be retrieved via the calendar API.
Use the GET /api/v1/vaults/:vaultId/calendar/due endpoint to pull the current list of overdue items. The response is sorted by how far past due each document is, with the most overdue first.
// Fetch all overdue documents for a vault — returns DueDocument[]
const overdue = await client.calendar.getDueDates(VAULT_ID, {
status: 'overdue', // 'overdue' | 'upcoming' | 'all'
});
console.log(`${overdue.length} overdue documents:`);
for (const item of overdue) {
const daysOverdue = Math.floor(
(Date.now() - new Date(item.dueAt).getTime()) / 86_400_000,
);
console.log(` [${daysOverdue}d overdue] ${item.title} — ${item.path}`);
}The overdue check worker runs every 15 minutes, so there can be a brief lag between a document's due date passing and it appearing in the overdue list. For real-time checks in scripts, compare each DueDocument.dueAt against the current date directly. Overdue alerts are also sent as in-app notifications if you have notifications enabled in Settings → Notifications.
The agenda view shows all documents with due dates sorted chronologically. Unlike the overdue list (which shows only past-due items), the agenda spans past, present, and future — giving you a complete picture of your project timeline.
Filter the agenda by date range to focus on what's due this week or this month. The agenda is the canonical view for project planning meetings.
In the UI: open Calendar → Agenda to see the list view. Each row shows the document title, vault, due date, and tags. Click any row to open the document.
// Fetch the upcoming agenda, grouped by week.
// getAgenda accepts optional { status, range, groupBy } filters and
// returns { groups, total } where each group has its own DueDocument[].
const agenda = await client.calendar.getAgenda(VAULT_ID, {
status: 'upcoming', // 'overdue' | 'upcoming' | 'all'
range: 'month', // e.g. 'week' | 'month'
groupBy: 'week', // group documents by 'day' | 'week' | 'month'
});
console.log(`${agenda.total} upcoming items:`);
for (const group of agenda.groups) {
console.log(`\n${group.label}`);
for (const item of group.items) {
const daysUntilDue = Math.ceil(
(new Date(item.dueAt).getTime() - Date.now()) / 86_400_000,
);
const label = daysUntilDue === 0 ? 'TODAY' : `in ${daysUntilDue}d`;
console.log(` [${label}] ${item.title}`);
}
}With external calendar sync, Lifestream Vault connects to your Google Calendar or Outlook account and performs bidirectional sync: documents with due dates appear as calendar events, and events you create in your calendar app can optionally create or update documents in your vault.
Sync is run by the calendar-sync.worker BullMQ worker and runs on a configurable schedule (default: every 30 minutes).
Supported providers:
// Calendar connectors live on the 'calendar' resource (there is no
// separate client.calendarConnectors resource).
// Step 1: Begin the OAuth flow — returns an authorization URL to open
const { authUrl } = await client.calendar.connectGoogleCalendar(VAULT_ID);
// For Outlook: await client.calendar.connectOutlookCalendar(VAULT_ID)
console.log('Open this URL to authorize Google Calendar access:');
console.log(authUrl);
// Step 2: The OAuth callback route completes the connection server-side.
// Afterwards, list and manage connectors:
const connectors = await client.calendar.listConnectors(VAULT_ID);
console.log('Connected calendars:', connectors.length);
// Trigger a manual sync
const connectorId = connectors[0].id;
await client.calendar.syncConnector(VAULT_ID, connectorId);
// Disconnect a calendar
// await client.calendar.disconnectConnector(VAULT_ID, connectorId);External calendar sync (Google Calendar and Outlook) requires a Pro or Business subscription. The calendar connector endpoints return a 403 on Free accounts. Upgrade in Settings → Subscription.
Every vault exposes an iCal feed — a standard .ics URL you can subscribe to from Apple Calendar, Google Calendar, Outlook, Fantastical, or any other calendar app. The feed contains all documents with due dates as all-day events.
The feed URL is authenticated with a per-vault iCal token. Generate it with client.calendar.generateICalToken(vaultId), which returns the ready-to-use feedUrl. This token is separate from your API key and can be revoked and regenerated without affecting other credentials.
Feed URL format:
GET /api/v1/vaults/:vaultId/calendar/feed.ics?token=<token>
You can subscribe to iCal feeds from multiple vaults simultaneously. Each vault generates its own unique feed URL, so you can combine project timelines across vaults in a single calendar app.
// Generate (or regenerate) the iCal token for a vault.
// The response includes a ready-to-use feedUrl.
const { feedUrl } = await client.calendar.generateICalToken(VAULT_ID);
console.log('Subscribe to this URL in your calendar app:');
console.log(feedUrl);
// Check whether a token already exists without regenerating it:
const status = await client.calendar.getICalTokenStatus(VAULT_ID);
console.log('Has iCal token:', status.hasToken);The icalToken is a URL-safe random string that acts as a bearer credential for the feed. It does not expire automatically — rotate it manually if you believe it has been compromised. Rotating the token immediately invalidates all existing subscriptions (calendar apps will show an error until the subscriber updates their URL). The token is stored in plain text on the Vault record alongside icalTokenCreatedAt.
Pick a consistent due date philosophy and stick to it across your vault:
due with a calendar reminderdue with a tag like target to distinguish them from hard deadlinesdue for the next occurrence and update it each time you complete the taskTags and due dates work best together. Tag documents with the project or quarter they belong to (e.g. q1-2026, sdk-v2, customer-x). The agenda view itself is available via the SDK (client.calendar.getAgenda) and the API; to scope a project timeline from the CLI, combine the due-date list with a tag-filtered search:
# Overdue/upcoming items for the vault (vaultId is positional)
lsvault calendar due <VAULT_ID> --status upcoming
# Find documents in a given project by tag
lsvault search "" --vault <VAULT_ID> --tags sdk-v2
Build a weekly planning habit around the agenda view:
due or add a completed: YYYY-MM-DD field)When computing due dates programmatically, always use a date library that handles daylight saving time correctly. Use addDays(date, N) from date-fns instead of date.setDate(date.getDate() + N) — the latter can produce off-by-one results near DST transitions.
import { addDays, format } from 'date-fns';
const nextWeek = addDays(new Date(), 7);
const dueDate = format(nextWeek, 'yyyy-MM-dd'); // safe: '2026-03-04'
The iCal feed URL contains a token that grants read access to all due-date events in your vault. Treat it like a password — do not share it publicly, do not commit it to a public repository, and rotate it if it leaks.
You now have a complete project tracking setup using Lifestream Vault's calendar features. Here are some natural next steps: