API reference
OverDue ships two developer primitives, intentionally generic so you can plug in whatever automation stack you already use (n8n, Zapier, OpenClaw, Hermes, a shell script): a token-authed REST surface at /api/v1/* and signed outbound webhooks for task and triage events. Generate a token and add a webhook in Settings → Developer.
Auth
Every /api/v1/* request needs an Authorization: Bearer <token> header. Tokens are scoped to a single user; the server identifies your account from the token. Tokens are shown once at creation and stored hashed (sha256). If you lose one, revoke it and generate a new one.
Each token is capped at 100 requests per minute. A 429 response includes a Retry-After header in seconds.
curl https://overdueapp.com/api/v1/me \
-H "Authorization: Bearer ovd_live_..."
# 200 OK
{
"user": { "id": "uuid", "display_name": null, "timezone": "America/New_York" },
"token": { "label": "n8n flow", "scopes": ["full_access"], ... }
}Endpoints
GET /api/v1/me
Identify the calling token. Returns the owner user and the token metadata.
GET /api/v1/tasks
List tasks for the token’s user. Query params: status (comma-separated subset of inbox,now,next,waiting,later,scheduled,done), category_id, limit (1-200, default 100), cursor (ISO timestamp, paginate by next_cursor).
POST /api/v1/tasks
Create a task. Required: title. Everything else is optional and defaults match the in-app create flow.
curl https://overdueapp.com/api/v1/tasks \
-H "Authorization: Bearer ovd_live_..." \
-H "Content-Type: application/json" \
-d '{
"title": "Draft Q3 board update",
"effort_min": 90,
"priority": 2,
"energy": "deep",
"revenue_imp": "indirect",
"due_at": "2026-05-26T17:00:00-04:00",
"earliest_start": "2026-05-22T09:00:00-04:00",
"source": "n8n",
"source_url": "https://my-n8n/workflow/42"
}'
# 201 Created
{ "task": { "id": "...", "title": "...", "status": "inbox", ... } }GET /api/v1/tasks/{id}
Read one task.
PATCH /api/v1/tasks/{id}
Update one task. Send only the fields you want to change. Setting status: "done" stamps completed_at and fires task.completed.
POST /api/v1/tasks/{id}/notes
Append a work-log entry to a task without overwriting existing notes. Built for agent write-back: after an automation does work on a task, it records what it did here. Each call appends a timestamped, attributed line ([ISO · author] text), so human notes and agent logs coexist. Body: text (required), author(optional, defaults to "agent").
curl https://overdueapp.com/api/v1/tasks/<id>/notes \
-H "Authorization: Bearer ovd_live_..." \
-H "Content-Type: application/json" \
-d '{ "text": "Drafted the renewal email, waiting on your review.", "author": "Lisa" }'POST /api/v1/tasks/bulk (Pro)
Insert up to 200 tasks in one call. Same per-item shape as POST /tasks; wrap under a tasks array. For migration scripts + LLM-emitted batches. Personal tier returns 403.
GET /api/v1/audit (Pro)
Read your account’s activity log: task creations, completions, voice ingests, scheduler runs. Default window is the last 30 days. Query params: since, until, kind, limit (1-500).
POST /api/v1/voice/items
Push an external action item into the triage queue. OverDue stores it as a one-item synthetic transcript (provider manual), so the in-app triage UI handles it the same way as a Fireflies/Fathom/Grain item.
curl https://overdueapp.com/api/v1/voice/items \
-H "Authorization: Bearer ovd_live_..." \
-H "Content-Type: application/json" \
-d '{
"title": "Send the renewal proposal to AcmeCo",
"transcript": "Optional full transcript or context for triage.",
"source_url": "https://my-recorder/...",
"suggested_window": "lis"
}'GET /api/v1/events
Read auto-scheduled placements over a window. Query params: from / to ISO datetimes (default: last 24h to next 7d), limit (1-500, default 200). Google Calendar events are deliberately not exposed here — read those directly from Google.
AI features (Pro)
Two endpoints powered by the server-side LLM. Available when ANTHROPIC_API_KEY or OPENAI_API_KEY is configured on the server; 503 otherwise.
GET /api/v1/events/{placement_id}/brief
AI prep note for a single scheduled placement — 2–4 bullets, ~120 words: first concrete action, shape of done, the risk to dodge, optional handoff. Generated on first read, cached on the row. POST with {"regen": true} forces a re-generation.
GET /api/v1/briefing
Today’s morning briefing: highest-stakes calendar item, anything overdue / at-risk, the shape of the day, plus a specific first-60-minutes suggestion. Pre-generated by a 6 AM ET cron for active Pro users; on-demand on first read otherwise. Optional ?date=YYYY-MM-DD for a past day.
CLI
For terminal-first power users. Read commands work on any tier; write commands (add, done, note) require Pro.
$ export OVERDUE_TOKEN=ovd_live_...
$ npx overdue today # today's scheduled blocks
$ npx overdue list --status=now --limit=5 # tasks (filterable)
$ npx overdue add "Draft Q3 deck" --priority=2 --due=2026-06-15 --energy=deep
$ npx overdue done 1a2b3c4d # accepts 8-char id prefix
$ npx overdue note 1a2b3c4d "Drafted; awaiting review" --author=Lisa
$ npx overdue me # confirm token + scopeSource is in the OverDue repo at bin/overdue.mjs (single file, zero dependencies, runs on Node 18+).
MCP server (Pro)
OverDue exposes itself as a Model Context Protocol (MCP) server so Claude Desktop, Cursor, Zed, Codex, or any MCP-aware client can plug in and call OverDue tools directly — no glue code. Pro-tier tokens only.
Endpoint: https://overdueapp.com/api/mcp (POST, JSON-RPC 2.0 with Streamable HTTP transport). Auth header is the same Authorization: Bearer ovd_… you use for the rest of the API.
Tools exposed:
list_tasks— read recent tasks (filter by status / category)create_task— add a task; the scheduler places it on the next tickupdate_task— modify any task field, including statuscomplete_task— mark a task done + stamp completed_atappend_note— add a timestamped work-log entry to a task without clobbering existing noteslist_schedule— read scheduled placements over a window
Claude Desktop config example:
{
"mcpServers": {
"overdue": {
"transport": {
"type": "streamable-http",
"url": "https://overdueapp.com/api/mcp",
"headers": { "Authorization": "Bearer ovd_live_..." }
}
}
}
}Category-scoped tokens propagate through MCP automatically: an LIS-only token can only read/write LIS tasks even when called from Claude.
Webhooks
OverDue POSTs a JSON payload to each registered webhook URL when a subscribed event fires. Failed deliveries (5xx, timeout, or any non-2xx) retry up to two times with backoff (+30s, +5min). After 100 consecutive failures the webhook auto-disables; re-enable it from the Developer page.
Payload
POST https://your-endpoint.example.com/overdue
Content-Type: application/json
User-Agent: OverDue-Webhooks/1
X-OverDue-Event: task.created
X-OverDue-Delivery: <uuid; same id across retries>
X-OverDue-Signature: sha256=<hex hmac of the request body>
{
"event": "task.created",
"user_id": "uuid",
"at": "2026-05-19T18:42:00.000Z",
"data": { "task": { "id": "...", "title": "...", ... } }
}Verifying the signature
Compute sha256=<hex hmac> of the raw request body using your webhook secret, then compare against the X-OverDue-Signature header. Use a constant-time comparison (e.g. crypto.timingSafeEqual).
// Node.js
import { createHmac, timingSafeEqual } from 'node:crypto';
function verify(rawBody, signatureHeader, secret) {
const expected = 'sha256=' + createHmac('sha256', secret)
.update(rawBody, 'utf8').digest('hex');
const a = Buffer.from(expected, 'utf8');
const b = Buffer.from(signatureHeader, 'utf8');
return a.length === b.length && timingSafeEqual(a, b);
}Event types
task.createdTask createdtask.completedTask completedtask.scheduledTask scheduledcheckin.preCheck-in: precheckin.postCheck-in: postcheckin.slipCheck-in: slippedbriefing.readyDaily briefing readyvoice_item.queuedVoice triage item queued
Category scoping
Both tokens and webhooks can be restricted to one or more categories. An unscoped token has full access to every task; a scoped token can only see, create, and modify tasks whose category_id is in its allowed set. Pick the categories at creation time from Settings → Developer.
Tokens: GET /api/v1/tasks filters to allowed categories. POST without a category_id auto-fills the first allowed one; an explicit category outside the set returns 403. PATCH to a task outside the set returns 404 (no existence leak), and moving a task into a category outside the set returns 403. GET /api/v1/events only returns placements for in-scope tasks. POST /api/v1/voice/items is exempt — triage items are pre-category, so the user (or another token) is the one who eventually picks one on accept.
Webhooks: for a scoped webhook, task.created, task.completed, and task.scheduled only fire when the task's category_id is in the allowed set. Events without category attribution (voice_item.queued, briefing.ready, checkin.*) are skipped — run a second unscoped webhook for those.
Confirm a token's scope by calling GET /api/v1/me — the response includes a token.categories array (or null for unrestricted).
Versioning
Everything under /api/v1/* is a stable contract. Breaking changes ship under a new prefix and the old one keeps working for at least six months. Additive changes (new optional fields, new event types) can land in v1 without notice — your client should ignore unknown fields and unknown event types.