Planitude

Remote MCP server of Planitude, a free online Gantt and project planning tool: ChatGPT, Claude, Gemini, Cursor or Claude Code read tasks, dates and critical path and update the plan. OAuth or API key.

Hosted MCP Server

npx add-mcp 'https://planitude.app/mcp'

Installs into Claude Code, Codex, Cursor and more

Documentation

Build with Planitude

A REST API, signed webhooks and an MCP server: keep your systems in sync with the plan, react to every change and let an AI assistant work on your projects.

Code, field names and the technical reference are in English, like most API documentation.

  • OpenAPI 3.1: https://planitude.app/api/openapi.json

  • Base URL: https://planitude.app

  • MCP: https://planitude.app/mcp

  • API version: 2026-10-01

  • Sync tasks with your systems: Create projects and tasks from a CRM, an ERP or a script, find them again by your own id with externalId and let the scheduling engine compute the dates.

  • React to every change: Receive a signed HTTPS request when a task is created, completed or moved, whether the change comes from the app, the API, an import or an AI assistant.

  • Connect an AI assistant: ChatGPT, Claude, Gemini, Cursor and VS Code read your plans and propose changes through the MCP server, with your permissions and nothing more.

Requests and responses are JSON over HTTPS. Durations and lags are in working minutes (an 8-hour day is 480), instants are ISO 8601 in UTC, dates without a time are YYYY-MM-DD.

Quickstart

From zero to your first webhook in three steps. All you need is a terminal.

1. Create an API key

Open Profile → API keys, choose Read and write and copy the key: it starts with pln_ and is shown only once. Keep it in an environment variable.

export PLANITUDE_API_KEY="pln_…"

2. Make your first call

List the projects you can see. The key acts as you: same projects, same roles.

curl https://planitude.app/api/projects \
  -H "Authorization: Bearer $PLANITUDE_API_KEY"

Expected result:

{
  "projects": [
    {
      "id": "40be00e0-7e34-4f7a-864b-5cc986f575b8",
      "workspaceId": "824650d2-d0b3-4cac-8df8-5743ae9ff65a",
      "key": "WHX",
      "keyLocked": false,
      "name": "Warehouse expansion",
      "description": null,
      "startDate": "2026-10-05",
      "calendarId": null,
      "currency": "EUR",
      "scheduleFrom": "forward",
      "deadline": null,
      "color": null,
      "settings": {},
      "archivedAt": null,
      "createdBy": "b1666432-132a-4ea9-b1ae-76ea69e226c5",
      "createdAt": "2026-10-01T08:30:12.418Z",
      "updatedAt": "2026-10-01T08:30:12.418Z",
      "workspaceName": "Acme Construction",
      "role": "owner",
      "taskCount": 12,
      "finishAt": "2026-10-07T15:00:00.000Z"
    }
  ]
}

3. Receive a webhook

Subscribe a public HTTPS endpoint to the events you need, then send a test event right away and verify its signature.

curl -X POST https://planitude.app/api/webhooks \
  -H "Authorization: Bearer $PLANITUDE_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "url": "https://example.com/hooks/planitude",
  "events": [
    "task.created",
    "task.completed"
  ],
  "projectId": "40be00e0-7e34-4f7a-864b-5cc986f575b8"
}'
{
  "id": "6f1c1d1e-2f4b-4d0a-9a77-2a3c5b1e9f10",
  "type": "webhook.ping",
  "apiVersion": "2026-10-01",
  "createdAt": "2026-10-01T08:30:12.418Z",
  "workspaceId": "824650d2-d0b3-4cac-8df8-5743ae9ff65a",
  "projectId": "40be00e0-7e34-4f7a-864b-5cc986f575b8",
  "actor": {
    "id": "b1666432-132a-4ea9-b1ae-76ea69e226c5",
    "name": "Anna Rossi"
  },
  "data": {
    "message": "Planitude webhook test",
    "webhook": {
      "id": "1747ee7f-a395-4d3c-94b0-54b513d71d88",
      "name": "Warehouse sync",
      "events": [
        "task.created",
        "task.completed"
      ]
    }
  }
}

Authentication

Authenticate every request with a personal API key in the Authorization header, as a bearer token. Keys are personal: they act as you, with your role on each project, never more. You can limit a key to one workspace, give it an expiry and revoke it at any time; every call is logged next to the key.

ScopeAllows
read (planitude:read)Read projects, tasks, dependencies, members, comments, baselines and webhooks.
write (planitude:write)Everything in read, plus create and change projects, tasks, dependencies, comments, baselines and webhooks.

Keys and OAuth apps cannot delete projects or tasks: deleting stays in the web app, where a person confirms it. A script that targets the wrong id cannot wipe a plan.

OAuth 2.1 for third-party apps

Apps that act on behalf of other people use OAuth 2.1: authorization code with PKCE (S256), refresh tokens rotated on every use, dynamic client registration (RFC 7591). Ask for the resource https://planitude.app/api and the scope planitude:read or planitude:write.

The endpoints are published in the standard discovery documents, so OAuth libraries configure themselves.

{
  "issuer": "https://planitude.app",
  "authorization_endpoint": "https://planitude.app/oauth/authorize",
  "token_endpoint": "https://planitude.app/api/oauth/token",
  "registration_endpoint": "https://planitude.app/api/oauth/register",
  "revocation_endpoint": "https://planitude.app/api/oauth/revoke",
  "scopes_supported": [
    "planitude:read",
    "planitude:write"
  ],
  "grant_types_supported": [
    "authorization_code",
    "refresh_token"
  ],
  "code_challenge_methods_supported": [
    "S256"
  ]
}

Good practices

  • Keep keys in environment variables or a secret manager, never in code or in a repository. Keys start with pln_, so secret scanners can spot them.
  • Use a read-only key when you only read, and one key per integration: you can revoke one without stopping the others.
  • Give keys used by scripts an expiry and rotate them.
  • Call the API from your server, not from a browser: a key in front-end code is a public key.

Errors

Planitude uses standard HTTP status codes. Errors always have the same JSON shape: a stable error code to branch on and, when useful, details with the reason or the fields to fix.

StatuserrorMeaning
400invalid_requestThe request is not valid: details.fields lists each field and the problem.
401unauthorizedThe key is missing, invalid, revoked or expired.
403forbiddenThe key or your role does not allow it (details.reason), for example a write with a read-only key, or a delete.
404not_foundNot found, or not visible to you.
409dependency_cycleConflict with the plan, for example a dependency that would close a cycle (dependency_cycle).
429rate_limitedToo many requests: wait for the seconds in Retry-After.
500server_errorSomething went wrong on our side: retry with exponential backoff.
FieldTypeRequiredDescription
errorstringyesStable error code: invalid_request, unauthorized, forbidden, not_found, conflict, dependency_cycle, limit_exceeded, rate_limited, server_error, ...
detailsobject
HTTP 400
{
  "error": "invalid_request",
  "details": {
    "fields": [
      {
        "path": "name",
        "message": "Invalid input: expected string, received undefined"
      },
      {
        "path": "durationMin",
        "message": "Too small: expected number to be >=0"
      }
    ]
  }
}
HTTP 403
{
  "error": "forbidden",
  "details": {
    "reason": "destructive"
  }
}
HTTP 409
{
  "error": "dependency_cycle",
  "details": {
    "cycles": [
      {
        "path": "Foundations -> Walls -> Foundations",
        "taskIds": [
          "4dbd104a-00cd-4035-8955-255f52ea586a",
          "a22fd799-c4dc-4869-a466-0dd6e96f71ce"
        ],
        "taskNames": [
          "Foundations",
          "Walls"
        ]
      }
    ]
  }
}

Rate limits

Limits apply per key, in a sliding window of one minute: 240 read requests and 60 write requests. Over the limit you get 429 rate_limited with a Retry-After header, in seconds.

Wait for Retry-After before retrying, spread bulk work over time and group many changes in one change set: up to 1,000 changes in one request and one recalculation.

HTTP/1.1 429 Too Many Requests
Retry-After: 12
Content-Type: application/json

{
  "error": "rate_limited",
  "details": {
    "retryAfterSec": 12
  }
}

Pagination and search

Task lists are paginated with a cursor. Pass limit (1–500, default 100) and, for the next page, the nextCursor of the previous response as cursor. When nextCursor is null there are no more pages.

Filter with externalId (exact match on your own id), q (name contains), updatedSince and completed; sort with sort=-number (newest first) or -updatedAt. Shorter lists, such as projects, members and dependencies, come back whole in one response.

const tasks = [];
let cursor = null;
do {
  const url = new URL("https://planitude.app/api/projects/40be00e0-7e34-4f7a-864b-5cc986f575b8/tasks");
  url.searchParams.set("limit", "500");
  if (cursor) url.searchParams.set("cursor", cursor);
  const res = await fetch(url, {
    headers: { Authorization: `Bearer ${process.env.PLANITUDE_API_KEY}` },
  });
  if (!res.ok) throw new Error(`${res.status} ${await res.text()}`);
  const page = await res.json();
  tasks.push(...page.tasks);
  cursor = page.nextCursor;
} while (cursor);
console.log(tasks.length);

Versioning

The API and the webhook payloads are versioned by date. The current version is 2026-10-01: it is the apiVersion field of every event and the info.version of the OpenAPI document.

Within a version we only add: new endpoints, new optional fields, new event types. Fields are never removed or retyped without a new version, so ignore the fields you do not know.

API reference

Every request: Authorization: Bearer $PLANITUDE_API_KEY. Examples use the same project (WHX), task and person throughout.

Account

Who is calling, and their workspaces.

User object

FieldTypeRequiredDescription
idstring (uuid)
emailstring (email)
namestring, nullable
avatarUrlstring, nullable
localestring
emailVerifiedboolean
{
  "id": "b1666432-132a-4ea9-b1ae-76ea69e226c5",
  "email": "anna@example.com",
  "name": "Anna Rossi",
  "avatarUrl": null,
  "locale": "en",
  "emailVerified": true
}

Workspace object

FieldTypeRequiredDescription
idstring (uuid)
namestring
rolestringOne of: owner, admin, member.
{
  "id": "824650d2-d0b3-4cac-8df8-5743ae9ff65a",
  "name": "Acme Construction",
  "role": "owner"
}

Current user (connection test)

GET /api/auth/me (scope: read)

curl https://planitude.app/api/auth/me \
  -H "Authorization: Bearer $PLANITUDE_API_KEY"

Returns 200 OK: The user

FieldTypeRequiredDescription
userUser
{
  "user": {
    "id": "b1666432-132a-4ea9-b1ae-76ea69e226c5",
    "email": "anna@example.com",
    "name": "Anna Rossi",
    "avatarUrl": null,
    "locale": "en",
    "emailVerified": true
  }
}

Workspaces of the user, with role

GET /api/workspaces (scope: read)

curl https://planitude.app/api/workspaces \
  -H "Authorization: Bearer $PLANITUDE_API_KEY"

Returns 200 OK: Workspaces

FieldTypeRequiredDescription
workspacesarray of Workspace
{
  "workspaces": [
    {
      "id": "824650d2-d0b3-4cac-8df8-5743ae9ff65a",
      "name": "Acme Construction",
      "role": "owner"
    }
  ]
}

Projects

Projects, templates and baselines.

Project object

A project as returned by the write endpoints and project listings.

FieldTypeRequiredDescription
idstring (uuid)
workspaceIdstring (uuid)
keystringProject key, e.g. CRON (task keys are CRON-12).
keyLockedboolean
namestring
descriptionstring, nullable
startDatestring (date)
calendarIdstring (uuid), nullable
currencystring
scheduleFromstringOne of: forward, backward.
deadlinestring (date), nullable
colorstring, nullable
settingsobject
archivedAtstring (date-time), nullable
createdBystring (uuid), nullable
createdAtstring (date-time)
updatedAtstring (date-time)
{
  "id": "40be00e0-7e34-4f7a-864b-5cc986f575b8",
  "workspaceId": "824650d2-d0b3-4cac-8df8-5743ae9ff65a",
  "key": "WHX",
  "keyLocked": false,
  "name": "Warehouse expansion",
  "description": null,
  "startDate": "2026-10-05",
  "calendarId": null,
  "currency": "EUR",
  "scheduleFrom": "forward",
  "deadline": null,
  "color": null,
  "settings": {},
  "archivedAt": null,
  "createdBy": "b1666432-132a-4ea9-b1ae-76ea69e226c5",
  "createdAt": "2026-10-01T08:30:12.418Z",
  "updatedAt": "2026-10-01T08:30:12.418Z"
}

Template object

FieldTypeRequiredDescription
idstringbuiltin:<slug> or the id of a template saved by you.
kindstringOne of: builtin, user.
namestring
descriptionstring, nullable
taskCountinteger
durationDaysintegerWorking days from start to finish.
{
  "id": "builtin:website-launch",
  "kind": "builtin",
  "name": "Website launch",
  "description": "From discovery to go-live: design, content, development, testing and launch.",
  "taskCount": 12,
  "durationDays": 43
}

Baseline object

FieldTypeRequiredDescription
idstring (uuid)
namestring
indexinteger, nullableNumbered MS Project baseline (1-10); null for the main baseline and the ones you name.
capturedAtstring (date-time)
capturedBystring (uuid), nullable
taskCountinteger
{
  "id": "b5d68d05-0640-48f9-8eeb-ed7804f4a116",
  "name": "Plan v1",
  "index": null,
  "capturedAt": "2026-10-01T08:30:12.418Z",
  "capturedBy": "b1666432-132a-4ea9-b1ae-76ea69e226c5",
  "taskCount": 12
}

List projects

GET /api/projects (scope: read)

All projects visible to the user (not paginated). ?archived=1 includes archived ones.

Query parameters:

FieldTypeRequiredDescription
archivedstringOne of: 1.
curl https://planitude.app/api/projects \
  -H "Authorization: Bearer $PLANITUDE_API_KEY"

Returns 200 OK: Projects

FieldTypeRequiredDescription
projectsarray of Project
{
  "projects": [
    {
      "id": "40be00e0-7e34-4f7a-864b-5cc986f575b8",
      "workspaceId": "824650d2-d0b3-4cac-8df8-5743ae9ff65a",
      "key": "WHX",
      "keyLocked": false,
      "name": "Warehouse expansion",
      "description": null,
      "startDate": "2026-10-05",
      "calendarId": null,
      "currency": "EUR",
      "scheduleFrom": "forward",
      "deadline": null,
      "color": null,
      "settings": {},
      "archivedAt": null,
      "createdBy": "b1666432-132a-4ea9-b1ae-76ea69e226c5",
      "createdAt": "2026-10-01T08:30:12.418Z",
      "updatedAt": "2026-10-01T08:30:12.418Z",
      "workspaceName": "Acme Construction",
      "role": "owner",
      "taskCount": 12,
      "finishAt": "2026-10-07T15:00:00.000Z"
    }
  ]
}

Create a project

POST /api/projects (scope: write)

Request body:

FieldTypeRequiredDescription
namestringyes
workspaceIdstring (uuid)Defaults to your personal workspace.
keystring2-5 letters/digits, starts with a letter. Derived from the name if omitted.
descriptionstring, nullable
startDatestring (date)
deadlinestring (date), nullable
currencystring
scheduleFromstringOne of: forward, backward.
colorstring, nullable
curl -X POST https://planitude.app/api/projects \
  -H "Authorization: Bearer $PLANITUDE_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"name":"Warehouse expansion","startDate":"2026-10-05","currency":"EUR"}'

Returns 201 Created: Created

FieldTypeRequiredDescription
projectProject
{
  "project": {
    "id": "40be00e0-7e34-4f7a-864b-5cc986f575b8",
    "workspaceId": "824650d2-d0b3-4cac-8df8-5743ae9ff65a",
    "key": "WHX",
    "keyLocked": false,
    "name": "Warehouse expansion",
    "description": null,
    "startDate": "2026-10-05",
    "calendarId": null,
    "currency": "EUR",
    "scheduleFrom": "forward",
    "deadline": null,
    "color": null,
    "settings": {},
    "archivedAt": null,
    "createdBy": "b1666432-132a-4ea9-b1ae-76ea69e226c5",
    "createdAt": "2026-10-01T08:30:12.418Z",
    "updatedAt": "2026-10-01T08:30:12.418Z"
  }
}

Create a project from a template

POST /api/projects/from-template (scope: write)

Request body:

FieldTypeRequiredDescription
templateIdstringyesId from GET /api/templates.
startDatestring (date)yes
namestring
keystring
localestringLanguage of the template texts (en, it, es, fr, de, pt, nl, pl, ja, zh, sv, da, no, fi, cs, ro, uk, tr, ar, he, hi, id, vi, ko).
tzstringIANA time zone of the project calendar.
workspaceIdstring (uuid)
curl -X POST https://planitude.app/api/projects/from-template \
  -H "Authorization: Bearer $PLANITUDE_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "templateId": "builtin:website-launch",
  "startDate": "2026-10-05",
  "name": "Website relaunch",
  "locale": "en"
}'

Returns 201 Created: Created

FieldTypeRequiredDescription
projectProject
taskCountinteger
conflictsarray of Conflict
{
  "project": {
    "id": "40be00e0-7e34-4f7a-864b-5cc986f575b8",
    "workspaceId": "824650d2-d0b3-4cac-8df8-5743ae9ff65a",
    "key": "WHX",
    "keyLocked": false,
    "name": "Warehouse expansion",
    "description": null,
    "startDate": "2026-10-05",
    "calendarId": null,
    "currency": "EUR",
    "scheduleFrom": "forward",
    "deadline": null,
    "color": null,
    "settings": {},
    "archivedAt": null,
    "createdBy": "b1666432-132a-4ea9-b1ae-76ea69e226c5",
    "createdAt": "2026-10-01T08:30:12.418Z",
    "updatedAt": "2026-10-01T08:30:12.418Z"
  },
  "taskCount": 12,
  "conflicts": []
}

List project templates

GET /api/templates (scope: read)

Query parameters:

FieldTypeRequiredDescription
localestring
curl https://planitude.app/api/templates \
  -H "Authorization: Bearer $PLANITUDE_API_KEY"

Returns 200 OK: Templates

FieldTypeRequiredDescription
templatesarray of Template
{
  "templates": [
    {
      "id": "builtin:website-launch",
      "kind": "builtin",
      "name": "Website launch",
      "description": "From discovery to go-live: design, content, development, testing and launch.",
      "taskCount": 12,
      "durationDays": 43
    }
  ]
}

Full project (tasks tree, dependencies, resources, members, baselines)

GET /api/projects/{projectId} (scope: read)

Path parameters:

FieldTypeRequiredDescription
projectIdstring (uuid)yesProject id.
curl https://planitude.app/api/projects/40be00e0-7e34-4f7a-864b-5cc986f575b8 \
  -H "Authorization: Bearer $PLANITUDE_API_KEY"

Returns 200 OK: Project

FieldTypeRequiredDescription
projectProject
tasksarray of Task
dependenciesarray of Dependency
{
  "project": {
    "id": "40be00e0-7e34-4f7a-864b-5cc986f575b8",
    "workspaceId": "824650d2-d0b3-4cac-8df8-5743ae9ff65a",
    "key": "WHX",
    "keyLocked": false,
    "name": "Warehouse expansion",
    "description": null,
    "startDate": "2026-10-05",
    "calendarId": null,
    "currency": "EUR",
    "scheduleFrom": "forward",
    "deadline": null,
    "color": null,
    "settings": {},
    "archivedAt": null,
    "createdBy": "b1666432-132a-4ea9-b1ae-76ea69e226c5",
    "createdAt": "2026-10-01T08:30:12.418Z",
    "updatedAt": "2026-10-01T08:30:12.418Z"
  },
  "tasks": [
    {
      "id": "4dbd104a-00cd-4035-8955-255f52ea586a",
      "number": 1,
      "key": "WHX-1",
      "parentId": null,
      "sortKey": "i",
      "name": "Foundations",
      "type": "task",
      "durationMin": 1440,
      "workMin": 0,
      "progressPct": 0,
      "constraintKind": "asap",
      "constraintDate": null,
      "deadline": null,
      "calendarId": null,
      "costFixed": 0,
      "notes": null,
      "color": null,
      "wbs": null,
      "externalId": "crm-4711",
      "startAt": "2026-10-05T06:00:00.000Z",
      "finishAt": "2026-10-07T15:00:00.000Z",
      "isCritical": true,
      "totalFloatMin": 0,
      "earlyStartAt": "2026-10-05T06:00:00.000Z",
      "lateFinishAt": "2026-10-07T15:00:00.000Z",
      "actualStartAt": null,
      "actualFinishAt": null,
      "actualDurationMin": null,
      "remainingDurationMin": null,
      "manuallyScheduled": false,
      "levelingDelayMin": 0,
      "taskType": "fixed_units",
      "effortDriven": false,
      "durationOptimisticMin": null,
      "durationPessimisticMin": null
    }
  ],
  "dependencies": [
    {
      "id": "9ba5faf8-923d-4ecc-86f0-f245c5d179db",
      "predecessorId": "4dbd104a-00cd-4035-8955-255f52ea586a",
      "successorId": "a22fd799-c4dc-4869-a466-0dd6e96f71ce",
      "type": "FS",
      "lagMin": 0
    }
  ]
}

Update a project

PATCH /api/projects/{projectId} (scope: write)

Path parameters:

FieldTypeRequiredDescription
projectIdstring (uuid)yesProject id.

Request body:

FieldTypeRequiredDescription
namestring
descriptionstring, nullable
startDatestring (date)
deadlinestring (date), nullable
currencystring
scheduleFromstringOne of: forward, backward.
colorstring, nullable
archivedboolean
curl -X PATCH https://planitude.app/api/projects/40be00e0-7e34-4f7a-864b-5cc986f575b8 \
  -H "Authorization: Bearer $PLANITUDE_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"deadline":"2026-12-18"}'

Returns 200 OK: Updated

FieldTypeRequiredDescription
projectProject
conflictsarray of Conflict
rescheduledbooleanThe schedule was recalculated (start date, calendar or direction changed).
{
  "project": {
    "id": "40be00e0-7e34-4f7a-864b-5cc986f575b8",
    "workspaceId": "824650d2-d0b3-4cac-8df8-5743ae9ff65a",
    "key": "WHX",
    "keyLocked": false,
    "name": "Warehouse expansion",
    "description": null,
    "startDate": "2026-10-05",
    "calendarId": null,
    "currency": "EUR",
    "scheduleFrom": "forward",
    "deadline": null,
    "color": null,
    "settings": {},
    "archivedAt": null,
    "createdBy": "b1666432-132a-4ea9-b1ae-76ea69e226c5",
    "createdAt": "2026-10-01T08:30:12.418Z",
    "updatedAt": "2026-10-01T08:30:12.418Z"
  },
  "conflicts": [],
  "rescheduled": false
}

Export the plan as a file (Microsoft Project XML, CSV, PNG or PDF)

GET /api/projects/{projectId}/export (scope: read)

Returns the file itself (Content-Disposition: attachment), not JSON. Any role with access to the project, read-only included.

png and pdf draw the Gantt chart on the server (title, generation date, legend; in the PDF repeated on every page) and accept the image options below; at most 20 image exports per user every 10 minutes. Over the size limits: 413 limit_exceeded with details.reason (tasks, image_too_large, pages, timeline).

Path parameters:

FieldTypeRequiredDescription
projectIdstring (uuid)yesProject id.

Query parameters:

FieldTypeRequiredDescription
formatstringFile format. mspdi = Microsoft Project XML. One of: mspdi, csv, png, pdf. Default: mspdi.
localestringLanguage of headings, legend and dates (e.g. en, it, ar, ja). Default: en.
scalestringPNG/PDF: time unit of the columns. One of: auto, day, week, month, quarter. Default: auto.
fromstring (date)PNG/PDF: first day to draw (with to). Default: the whole project.
tostring (date)PNG/PDF: last day to draw (with from).
criticalstringPNG/PDF: highlight the critical path. One of: 0, 1. Default: 1.
baselinestringPNG/PDF: draw the main baseline under each bar. One of: 0, 1. Default: 1.
linksstringPNG/PDF: draw dependency arrows. One of: 0, 1. Default: 1.
paperstringPDF: paper size. One of: a4, a3, letter. Default: a4.
orientationstringPDF: page orientation. One of: landscape, portrait. Default: landscape.
curl "https://planitude.app/api/projects/40be00e0-7e34-4f7a-864b-5cc986f575b8/export?format=pdf&locale=en&paper=a4" \
  -H "Authorization: Bearer $PLANITUDE_API_KEY" \
  -o plan.pdf

Returns 200 OK: The file: application/xml, text/csv, image/png or application/pdf.

List baselines

GET /api/projects/{projectId}/baseline (scope: read)

Path parameters:

FieldTypeRequiredDescription
projectIdstring (uuid)yesProject id.
curl https://planitude.app/api/projects/40be00e0-7e34-4f7a-864b-5cc986f575b8/baseline \
  -H "Authorization: Bearer $PLANITUDE_API_KEY"

Returns 200 OK: Baselines

FieldTypeRequiredDescription
baselinesarray of Baseline
{
  "baselines": [
    {
      "id": "b5d68d05-0640-48f9-8eeb-ed7804f4a116",
      "name": "Plan v1",
      "index": null,
      "capturedAt": "2026-10-01T08:30:12.418Z",
      "capturedBy": "b1666432-132a-4ea9-b1ae-76ea69e226c5",
      "taskCount": 12
    }
  ]
}

Save a baseline

POST /api/projects/{projectId}/baseline (scope: write)

Path parameters:

FieldTypeRequiredDescription
projectIdstring (uuid)yesProject id.

Request body:

FieldTypeRequiredDescription
namestring
curl -X POST https://planitude.app/api/projects/40be00e0-7e34-4f7a-864b-5cc986f575b8/baseline \
  -H "Authorization: Bearer $PLANITUDE_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"name":"Plan v1"}'

Returns 201 Created: Saved

FieldTypeRequiredDescription
baselineBaseline
{
  "baseline": {
    "id": "b5d68d05-0640-48f9-8eeb-ed7804f4a116",
    "name": "Plan v1",
    "index": null,
    "capturedAt": "2026-10-01T08:30:12.418Z",
    "capturedBy": "b1666432-132a-4ea9-b1ae-76ea69e226c5",
    "taskCount": 12
  }
}

Tasks

Tasks, search by external id, comments.

Task object

Task representation of GET /tasks and webhook payloads: the stored task (without sortKey) plus project, assignees and link.

FieldTypeRequiredDescription
idstring (uuid)
numberintegerSequential number in the project, never reused.
keystringHuman key, e.g. CRON-12.
parentIdstring (uuid), nullable
namestring
typestringOne of: summary, task, milestone.
durationMinintegerDuration in working minutes (8h day = 480).
workMininteger
progressPctnumber
constraintKindstringOne of: asap, alap, start_no_earlier_than, start_no_later_than, finish_no_earlier_than, finish_no_later_than, must_start_on, must_finish_on.
constraintDatestring (date-time), nullable
deadlinestring (date-time), nullable
calendarIdstring (uuid), nullable
costFixednumber
notesstring, nullable
colorstring, nullable
wbsstring, nullable
externalIdstring, nullableYour identifier for this task in another system. Searchable with GET /tasks?externalId=.
startAtstring (date-time), nullableComputed start.
finishAtstring (date-time), nullableComputed finish.
isCriticalbooleanOn the critical path.
totalFloatMininteger, nullableTotal float (slack) in working minutes.
earlyStartAtstring (date-time), nullable
lateFinishAtstring (date-time), nullable
actualStartAtstring (date-time), nullable
actualFinishAtstring (date-time), nullable
actualDurationMininteger, nullable
remainingDurationMininteger, nullable
manuallyScheduledboolean
levelingDelayMininteger
taskTypestringOne of: fixed_units, fixed_duration, fixed_work, fixed_duration_and_units.
effortDrivenboolean
durationOptimisticMininteger, nullable
durationPessimisticMininteger, nullable
projectIdstring (uuid)
completedbooleanprogressPct = 100.
assigneesarray of Assignee
urlstring (uri)Page of the task in the web app.
{
  "id": "4dbd104a-00cd-4035-8955-255f52ea586a",
  "number": 1,
  "key": "WHX-1",
  "parentId": null,
  "name": "Foundations",
  "type": "task",
  "durationMin": 1440,
  "workMin": 0,
  "progressPct": 0,
  "constraintKind": "asap",
  "constraintDate": null,
  "deadline": null,
  "calendarId": null,
  "costFixed": 0,
  "notes": null,
  "color": null,
  "wbs": null,
  "externalId": "crm-4711",
  "startAt": "2026-10-05T06:00:00.000Z",
  "finishAt": "2026-10-07T15:00:00.000Z",
  "isCritical": true,
  "totalFloatMin": 0,
  "earlyStartAt": "2026-10-05T06:00:00.000Z",
  "lateFinishAt": "2026-10-07T15:00:00.000Z",
  "actualStartAt": null,
  "actualFinishAt": null,
  "actualDurationMin": null,
  "remainingDurationMin": null,
  "manuallyScheduled": false,
  "levelingDelayMin": 0,
  "taskType": "fixed_units",
  "effortDriven": false,
  "durationOptimisticMin": null,
  "durationPessimisticMin": null,
  "projectId": "40be00e0-7e34-4f7a-864b-5cc986f575b8",
  "completed": false,
  "assignees": [
    {
      "resourceId": "0c6a8f52-3d7e-4b19-9a4e-6f1d2c8b7e30",
      "userId": "b1666432-132a-4ea9-b1ae-76ea69e226c5",
      "name": "Anna Rossi",
      "unitsPct": 100
    }
  ],
  "url": "https://planitude.app/app/t/WHX-1"
}

WriteResult object

FieldTypeRequiredDescription
tasksarray of TaskCreated/updated tasks plus the ones the recalculation moved.
deletedTaskIdsarray of string
dependenciesarray of Dependency
deletedDependencyIdsarray of string
conflictsarray of Conflict
projectStartAtstring (date-time)
projectFinishAtstring (date-time)
statsobject
stats.taskCountinteger
stats.recomputedCountintegerTasks the scheduling engine recalculated.
stats.savedCountintegerTasks whose stored values changed.
stats.durationMsinteger
{
  "tasks": [
    {
      "id": "4dbd104a-00cd-4035-8955-255f52ea586a",
      "number": 1,
      "key": "WHX-1",
      "parentId": null,
      "sortKey": "i",
      "name": "Foundations",
      "type": "task",
      "durationMin": 1440,
      "workMin": 0,
      "progressPct": 0,
      "constraintKind": "asap",
      "constraintDate": null,
      "deadline": null,
      "calendarId": null,
      "costFixed": 0,
      "notes": null,
      "color": null,
      "wbs": null,
      "externalId": "crm-4711",
      "startAt": "2026-10-05T06:00:00.000Z",
      "finishAt": "2026-10-07T15:00:00.000Z",
      "isCritical": true,
      "totalFloatMin": 0,
      "earlyStartAt": "2026-10-05T06:00:00.000Z",
      "lateFinishAt": "2026-10-07T15:00:00.000Z",
      "actualStartAt": null,
      "actualFinishAt": null,
      "actualDurationMin": null,
      "remainingDurationMin": null,
      "manuallyScheduled": false,
      "levelingDelayMin": 0,
      "taskType": "fixed_units",
      "effortDriven": false,
      "durationOptimisticMin": null,
      "durationPessimisticMin": null
    }
  ],
  "deletedTaskIds": [],
  "dependencies": [
    {
      "id": "9ba5faf8-923d-4ecc-86f0-f245c5d179db",
      "predecessorId": "4dbd104a-00cd-4035-8955-255f52ea586a",
      "successorId": "a22fd799-c4dc-4869-a466-0dd6e96f71ce",
      "type": "FS",
      "lagMin": 0
    }
  ],
  "deletedDependencyIds": [],
  "conflicts": [],
  "projectStartAt": "2026-10-05T06:00:00.000Z",
  "projectFinishAt": "2026-10-07T15:00:00.000Z",
  "stats": {
    "taskCount": 12,
    "recomputedCount": 3,
    "savedCount": 2,
    "durationMs": 39
  }
}

Comment object

FieldTypeRequiredDescription
idstring (uuid)
taskIdstring (uuid)
userIdstring (uuid)
authorNamestring
authorAvatarUrlstring, nullable
bodystring
mentionsarray of string
createdAtstring (date-time)
editedAtstring (date-time), nullable
{
  "id": "0fc2dec5-a409-47c9-9c6e-0663e8f8bcf3",
  "taskId": "4dbd104a-00cd-4035-8955-255f52ea586a",
  "userId": "b1666432-132a-4ea9-b1ae-76ea69e226c5",
  "authorName": "Anna Rossi",
  "authorAvatarUrl": null,
  "body": "Concrete delivered, we can start the walls.",
  "mentions": [],
  "createdAt": "2026-10-01T08:30:12.418Z",
  "editedAt": null
}

List or search tasks (paginated)

GET /api/projects/{projectId}/tasks (scope: read)

Path parameters:

FieldTypeRequiredDescription
projectIdstring (uuid)yesProject id.

Query parameters:

FieldTypeRequiredDescription
limitinteger1-500. Default: 100.
cursorstringnextCursor of the previous page.
sortstring-number = newest first (polling triggers). -updatedAt returns the first page only. One of: number, -number, -updatedAt. Default: number.
externalIdstringExact match on externalId.
qstringName contains (case-insensitive).
updatedSincestring (date-time)
completedstringOne of: true, false.
curl "https://planitude.app/api/projects/40be00e0-7e34-4f7a-864b-5cc986f575b8/tasks?externalId=crm-4711" \
  -H "Authorization: Bearer $PLANITUDE_API_KEY"

Returns 200 OK: A page of tasks

FieldTypeRequiredDescription
tasksarray of Task
nextCursorstring, nullable
{
  "tasks": [
    {
      "id": "4dbd104a-00cd-4035-8955-255f52ea586a",
      "number": 1,
      "key": "WHX-1",
      "parentId": null,
      "name": "Foundations",
      "type": "task",
      "durationMin": 1440,
      "workMin": 0,
      "progressPct": 0,
      "constraintKind": "asap",
      "constraintDate": null,
      "deadline": null,
      "calendarId": null,
      "costFixed": 0,
      "notes": null,
      "color": null,
      "wbs": null,
      "externalId": "crm-4711",
      "startAt": "2026-10-05T06:00:00.000Z",
      "finishAt": "2026-10-07T15:00:00.000Z",
      "isCritical": true,
      "totalFloatMin": 0,
      "earlyStartAt": "2026-10-05T06:00:00.000Z",
      "lateFinishAt": "2026-10-07T15:00:00.000Z",
      "actualStartAt": null,
      "actualFinishAt": null,
      "actualDurationMin": null,
      "remainingDurationMin": null,
      "manuallyScheduled": false,
      "levelingDelayMin": 0,
      "taskType": "fixed_units",
      "effortDriven": false,
      "durationOptimisticMin": null,
      "durationPessimisticMin": null,
      "projectId": "40be00e0-7e34-4f7a-864b-5cc986f575b8",
      "completed": false,
      "assignees": [
        {
          "resourceId": "0c6a8f52-3d7e-4b19-9a4e-6f1d2c8b7e30",
          "userId": "b1666432-132a-4ea9-b1ae-76ea69e226c5",
          "name": "Anna Rossi",
          "unitsPct": 100
        }
      ],
      "url": "https://planitude.app/app/t/WHX-1"
    }
  ],
  "nextCursor": null
}

Create a task, or apply a change set

POST /api/projects/{projectId}/tasks (scope: write)

Send a TaskCreate to create one task (201), or a ChangeSet to apply several changes in one transaction (200). Dates are recalculated by the scheduling engine.

Path parameters:

FieldTypeRequiredDescription
projectIdstring (uuid)yesProject id.

Request body (TaskCreate):

FieldTypeRequiredDescription
namestringyes
parentIdstring (uuid), nullableParent (summary) task; null = top level.
afterTaskIdstring (uuid), nullableInsert after this sibling.
beforeTaskIdstring (uuid), nullableInsert before this sibling.
typestringOne of: summary, task, milestone.
durationMinintegerDuration in working minutes (8h day = 480).
workMininteger
progressPctnumber
constraintKindstringOne of: asap, alap, start_no_earlier_than, start_no_later_than, finish_no_earlier_than, finish_no_later_than, must_start_on, must_finish_on.
constraintDatestring (date-time), nullable
deadlinestring (date-time), nullable
calendarIdstring (uuid), nullable
costFixednumber
notesstring, nullable
colorstring, nullable
wbsstring, nullable
externalIdstring, nullableYour identifier for this task in another system. Searchable with GET /tasks?externalId=.

Request body (or ChangeSet):

FieldTypeRequiredDescription
createTasksarray of TaskCreate
updateTasksarray of any
createDependenciesarray of DependencyCreate
deleteDependencyIdsarray of string
fullRescheduleboolean
curl -X POST https://planitude.app/api/projects/40be00e0-7e34-4f7a-864b-5cc986f575b8/tasks \
  -H "Authorization: Bearer $PLANITUDE_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"name":"Foundations","durationMin":1440,"externalId":"crm-4711"}'

Returns 201 Created: Task created

FieldTypeRequiredDescription
tasksarray of TaskCreated/updated tasks plus the ones the recalculation moved.
deletedTaskIdsarray of string
dependenciesarray of Dependency
deletedDependencyIdsarray of string
conflictsarray of Conflict
projectStartAtstring (date-time)
projectFinishAtstring (date-time)
statsobject
stats.taskCountinteger
stats.recomputedCountintegerTasks the scheduling engine recalculated.
stats.savedCountintegerTasks whose stored values changed.
stats.durationMsinteger
{
  "tasks": [
    {
      "id": "4dbd104a-00cd-4035-8955-255f52ea586a",
      "number": 1,
      "key": "WHX-1",
      "parentId": null,
      "sortKey": "i",
      "name": "Foundations",
      "type": "task",
      "durationMin": 1440,
      "workMin": 0,
      "progressPct": 0,
      "constraintKind": "asap",
      "constraintDate": null,
      "deadline": null,
      "calendarId": null,
      "costFixed": 0,
      "notes": null,
      "color": null,
      "wbs": null,
      "externalId": "crm-4711",
      "startAt": "2026-10-05T06:00:00.000Z",
      "finishAt": "2026-10-07T15:00:00.000Z",
      "isCritical": true,
      "totalFloatMin": 0,
      "earlyStartAt": "2026-10-05T06:00:00.000Z",
      "lateFinishAt": "2026-10-07T15:00:00.000Z",
      "actualStartAt": null,
      "actualFinishAt": null,
      "actualDurationMin": null,
      "remainingDurationMin": null,
      "manuallyScheduled": false,
      "levelingDelayMin": 0,
      "taskType": "fixed_units",
      "effortDriven": false,
      "durationOptimisticMin": null,
      "durationPessimisticMin": null
    }
  ],
  "deletedTaskIds": [],
  "dependencies": [
    {
      "id": "9ba5faf8-923d-4ecc-86f0-f245c5d179db",
      "predecessorId": "4dbd104a-00cd-4035-8955-255f52ea586a",
      "successorId": "a22fd799-c4dc-4869-a466-0dd6e96f71ce",
      "type": "FS",
      "lagMin": 0
    }
  ],
  "deletedDependencyIds": [],
  "conflicts": [],
  "projectStartAt": "2026-10-05T06:00:00.000Z",
  "projectFinishAt": "2026-10-07T15:00:00.000Z",
  "stats": {
    "taskCount": 12,
    "recomputedCount": 3,
    "savedCount": 2,
    "durationMs": 39
  }
}

Get a task

GET /api/projects/{projectId}/tasks/{taskId} (scope: read)

Path parameters:

FieldTypeRequiredDescription
projectIdstring (uuid)yesProject id.
taskIdstring (uuid)yesTask id.
curl https://planitude.app/api/projects/40be00e0-7e34-4f7a-864b-5cc986f575b8/tasks/4dbd104a-00cd-4035-8955-255f52ea586a \
  -H "Authorization: Bearer $PLANITUDE_API_KEY"

Returns 200 OK: The task

FieldTypeRequiredDescription
taskTask
{
  "task": {
    "id": "4dbd104a-00cd-4035-8955-255f52ea586a",
    "number": 1,
    "key": "WHX-1",
    "parentId": null,
    "name": "Foundations",
    "type": "task",
    "durationMin": 1440,
    "workMin": 0,
    "progressPct": 0,
    "constraintKind": "asap",
    "constraintDate": null,
    "deadline": null,
    "calendarId": null,
    "costFixed": 0,
    "notes": null,
    "color": null,
    "wbs": null,
    "externalId": "crm-4711",
    "startAt": "2026-10-05T06:00:00.000Z",
    "finishAt": "2026-10-07T15:00:00.000Z",
    "isCritical": true,
    "totalFloatMin": 0,
    "earlyStartAt": "2026-10-05T06:00:00.000Z",
    "lateFinishAt": "2026-10-07T15:00:00.000Z",
    "actualStartAt": null,
    "actualFinishAt": null,
    "actualDurationMin": null,
    "remainingDurationMin": null,
    "manuallyScheduled": false,
    "levelingDelayMin": 0,
    "taskType": "fixed_units",
    "effortDriven": false,
    "durationOptimisticMin": null,
    "durationPessimisticMin": null,
    "projectId": "40be00e0-7e34-4f7a-864b-5cc986f575b8",
    "completed": false,
    "assignees": [
      {
        "resourceId": "0c6a8f52-3d7e-4b19-9a4e-6f1d2c8b7e30",
        "userId": "b1666432-132a-4ea9-b1ae-76ea69e226c5",
        "name": "Anna Rossi",
        "unitsPct": 100
      }
    ],
    "url": "https://planitude.app/app/t/WHX-1"
  }
}

Update a task

PATCH /api/projects/{projectId}/tasks/{taskId} (scope: write)

Only the fields you send change; null clears a field.

Path parameters:

FieldTypeRequiredDescription
projectIdstring (uuid)yesProject id.
taskIdstring (uuid)yesTask id.

Request body:

FieldTypeRequiredDescription
namestring
typestringOne of: summary, task, milestone.
durationMinintegerDuration in working minutes (8h day = 480).
workMininteger
progressPctnumber
constraintKindstringOne of: asap, alap, start_no_earlier_than, start_no_later_than, finish_no_earlier_than, finish_no_later_than, must_start_on, must_finish_on.
constraintDatestring (date-time), nullable
deadlinestring (date-time), nullable
calendarIdstring (uuid), nullable
costFixednumber
notesstring, nullable
colorstring, nullable
wbsstring, nullable
externalIdstring, nullableYour identifier for this task in another system. Searchable with GET /tasks?externalId=.
placementobject
placement.parentIdstring (uuid), nullableParent (summary) task; null = top level.
placement.afterTaskIdstring (uuid), nullableInsert after this sibling.
placement.beforeTaskIdstring (uuid), nullableInsert before this sibling.
curl -X PATCH https://planitude.app/api/projects/40be00e0-7e34-4f7a-864b-5cc986f575b8/tasks/4dbd104a-00cd-4035-8955-255f52ea586a \
  -H "Authorization: Bearer $PLANITUDE_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"progressPct":100}'

Returns 200 OK: Updated

FieldTypeRequiredDescription
tasksarray of TaskCreated/updated tasks plus the ones the recalculation moved.
deletedTaskIdsarray of string
dependenciesarray of Dependency
deletedDependencyIdsarray of string
conflictsarray of Conflict
projectStartAtstring (date-time)
projectFinishAtstring (date-time)
statsobject
stats.taskCountinteger
stats.recomputedCountintegerTasks the scheduling engine recalculated.
stats.savedCountintegerTasks whose stored values changed.
stats.durationMsinteger
{
  "tasks": [
    {
      "id": "4dbd104a-00cd-4035-8955-255f52ea586a",
      "number": 1,
      "key": "WHX-1",
      "parentId": null,
      "sortKey": "i",
      "name": "Foundations",
      "type": "task",
      "durationMin": 1440,
      "workMin": 0,
      "progressPct": 0,
      "constraintKind": "asap",
      "constraintDate": null,
      "deadline": null,
      "calendarId": null,
      "costFixed": 0,
      "notes": null,
      "color": null,
      "wbs": null,
      "externalId": "crm-4711",
      "startAt": "2026-10-05T06:00:00.000Z",
      "finishAt": "2026-10-07T15:00:00.000Z",
      "isCritical": true,
      "totalFloatMin": 0,
      "earlyStartAt": "2026-10-05T06:00:00.000Z",
      "lateFinishAt": "2026-10-07T15:00:00.000Z",
      "actualStartAt": null,
      "actualFinishAt": null,
      "actualDurationMin": null,
      "remainingDurationMin": null,
      "manuallyScheduled": false,
      "levelingDelayMin": 0,
      "taskType": "fixed_units",
      "effortDriven": false,
      "durationOptimisticMin": null,
      "durationPessimisticMin": null
    }
  ],
  "deletedTaskIds": [],
  "dependencies": [
    {
      "id": "9ba5faf8-923d-4ecc-86f0-f245c5d179db",
      "predecessorId": "4dbd104a-00cd-4035-8955-255f52ea586a",
      "successorId": "a22fd799-c4dc-4869-a466-0dd6e96f71ce",
      "type": "FS",
      "lagMin": 0
    }
  ],
  "deletedDependencyIds": [],
  "conflicts": [],
  "projectStartAt": "2026-10-05T06:00:00.000Z",
  "projectFinishAt": "2026-10-07T15:00:00.000Z",
  "stats": {
    "taskCount": 12,
    "recomputedCount": 3,
    "savedCount": 2,
    "durationMs": 39
  }
}

Comments of a task

GET /api/projects/{projectId}/tasks/{taskId}/comments (scope: read)

Path parameters:

FieldTypeRequiredDescription
projectIdstring (uuid)yesProject id.
taskIdstring (uuid)yesTask id.
curl https://planitude.app/api/projects/40be00e0-7e34-4f7a-864b-5cc986f575b8/tasks/4dbd104a-00cd-4035-8955-255f52ea586a/comments \
  -H "Authorization: Bearer $PLANITUDE_API_KEY"

Returns 200 OK: Comments

FieldTypeRequiredDescription
commentsarray of Comment
{
  "comments": [
    {
      "id": "0fc2dec5-a409-47c9-9c6e-0663e8f8bcf3",
      "taskId": "4dbd104a-00cd-4035-8955-255f52ea586a",
      "userId": "b1666432-132a-4ea9-b1ae-76ea69e226c5",
      "authorName": "Anna Rossi",
      "authorAvatarUrl": null,
      "body": "Concrete delivered, we can start the walls.",
      "mentions": [],
      "createdAt": "2026-10-01T08:30:12.418Z",
      "editedAt": null
    }
  ]
}

Comment on a task

POST /api/projects/{projectId}/tasks/{taskId}/comments (scope: write)

Path parameters:

FieldTypeRequiredDescription
projectIdstring (uuid)yesProject id.
taskIdstring (uuid)yesTask id.

Request body:

FieldTypeRequiredDescription
bodystringyesPlain text.
mentionsarray of stringUser ids to notify.
curl -X POST https://planitude.app/api/projects/40be00e0-7e34-4f7a-864b-5cc986f575b8/tasks/4dbd104a-00cd-4035-8955-255f52ea586a/comments \
  -H "Authorization: Bearer $PLANITUDE_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"body":"Concrete delivered, we can start the walls."}'

Returns 201 Created: Created

FieldTypeRequiredDescription
commentComment
{
  "comment": {
    "id": "0fc2dec5-a409-47c9-9c6e-0663e8f8bcf3",
    "taskId": "4dbd104a-00cd-4035-8955-255f52ea586a",
    "userId": "b1666432-132a-4ea9-b1ae-76ea69e226c5",
    "authorName": "Anna Rossi",
    "authorAvatarUrl": null,
    "body": "Concrete delivered, we can start the walls.",
    "mentions": [],
    "createdAt": "2026-10-01T08:30:12.418Z",
    "editedAt": null
  }
}

Dependencies

Links between tasks (FS, SS, FF, SF, with lag).

Dependency object

FieldTypeRequiredDescription
idstring (uuid)
predecessorIdstring (uuid)
successorIdstring (uuid)
typestringOne of: FS, SS, FF, SF.
lagMinintegerWorking minutes; negative = lead.
{
  "id": "9ba5faf8-923d-4ecc-86f0-f245c5d179db",
  "predecessorId": "4dbd104a-00cd-4035-8955-255f52ea586a",
  "successorId": "a22fd799-c4dc-4869-a466-0dd6e96f71ce",
  "type": "FS",
  "lagMin": 0
}

List dependencies

GET /api/projects/{projectId}/dependencies (scope: read)

Path parameters:

FieldTypeRequiredDescription
projectIdstring (uuid)yesProject id.
curl https://planitude.app/api/projects/40be00e0-7e34-4f7a-864b-5cc986f575b8/dependencies \
  -H "Authorization: Bearer $PLANITUDE_API_KEY"

Returns 200 OK: Dependencies

FieldTypeRequiredDescription
dependenciesarray of Dependency
{
  "dependencies": [
    {
      "id": "9ba5faf8-923d-4ecc-86f0-f245c5d179db",
      "predecessorId": "4dbd104a-00cd-4035-8955-255f52ea586a",
      "successorId": "a22fd799-c4dc-4869-a466-0dd6e96f71ce",
      "type": "FS",
      "lagMin": 0
    }
  ]
}

Create (or update) a dependency

POST /api/projects/{projectId}/dependencies (scope: write)

A dependency that would close a cycle is refused with 409 dependency_cycle.

Path parameters:

FieldTypeRequiredDescription
projectIdstring (uuid)yesProject id.

Request body (DependencyCreate):

FieldTypeRequiredDescription
predecessorIdstring (uuid)yes
successorIdstring (uuid)yes
typestringOne of: FS, SS, FF, SF. Default: FS.
lagMinintegerDefault: 0.

Request body (or dependencies):

FieldTypeRequiredDescription
dependenciesarray of DependencyCreateyes
curl -X POST https://planitude.app/api/projects/40be00e0-7e34-4f7a-864b-5cc986f575b8/dependencies \
  -H "Authorization: Bearer $PLANITUDE_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "predecessorId": "4dbd104a-00cd-4035-8955-255f52ea586a",
  "successorId": "a22fd799-c4dc-4869-a466-0dd6e96f71ce",
  "type": "FS",
  "lagMin": 0
}'

Returns 201 Created: Created

FieldTypeRequiredDescription
tasksarray of TaskCreated/updated tasks plus the ones the recalculation moved.
deletedTaskIdsarray of string
dependenciesarray of Dependency
deletedDependencyIdsarray of string
conflictsarray of Conflict
projectStartAtstring (date-time)
projectFinishAtstring (date-time)
statsobject
stats.taskCountinteger
stats.recomputedCountintegerTasks the scheduling engine recalculated.
stats.savedCountintegerTasks whose stored values changed.
stats.durationMsinteger
{
  "tasks": [
    {
      "id": "4dbd104a-00cd-4035-8955-255f52ea586a",
      "number": 1,
      "key": "WHX-1",
      "parentId": null,
      "sortKey": "i",
      "name": "Foundations",
      "type": "task",
      "durationMin": 1440,
      "workMin": 0,
      "progressPct": 0,
      "constraintKind": "asap",
      "constraintDate": null,
      "deadline": null,
      "calendarId": null,
      "costFixed": 0,
      "notes": null,
      "color": null,
      "wbs": null,
      "externalId": "crm-4711",
      "startAt": "2026-10-05T06:00:00.000Z",
      "finishAt": "2026-10-07T15:00:00.000Z",
      "isCritical": true,
      "totalFloatMin": 0,
      "earlyStartAt": "2026-10-05T06:00:00.000Z",
      "lateFinishAt": "2026-10-07T15:00:00.000Z",
      "actualStartAt": null,
      "actualFinishAt": null,
      "actualDurationMin": null,
      "remainingDurationMin": null,
      "manuallyScheduled": false,
      "levelingDelayMin": 0,
      "taskType": "fixed_units",
      "effortDriven": false,
      "durationOptimisticMin": null,
      "durationPessimisticMin": null
    }
  ],
  "deletedTaskIds": [],
  "dependencies": [
    {
      "id": "9ba5faf8-923d-4ecc-86f0-f245c5d179db",
      "predecessorId": "4dbd104a-00cd-4035-8955-255f52ea586a",
      "successorId": "a22fd799-c4dc-4869-a466-0dd6e96f71ce",
      "type": "FS",
      "lagMin": 0
    }
  ],
  "deletedDependencyIds": [],
  "conflicts": [],
  "projectStartAt": "2026-10-05T06:00:00.000Z",
  "projectFinishAt": "2026-10-07T15:00:00.000Z",
  "stats": {
    "taskCount": 12,
    "recomputedCount": 3,
    "savedCount": 2,
    "durationMs": 39
  }
}

Remove a dependency

DELETE /api/projects/{projectId}/dependencies/{dependencyId} (scope: write)

Path parameters:

FieldTypeRequiredDescription
projectIdstring (uuid)yesProject id.
dependencyIdstring (uuid)yes
curl -X DELETE https://planitude.app/api/projects/40be00e0-7e34-4f7a-864b-5cc986f575b8/dependencies/9ba5faf8-923d-4ecc-86f0-f245c5d179db \
  -H "Authorization: Bearer $PLANITUDE_API_KEY"

Returns 200 OK: Removed

FieldTypeRequiredDescription
tasksarray of TaskCreated/updated tasks plus the ones the recalculation moved.
deletedTaskIdsarray of string
dependenciesarray of Dependency
deletedDependencyIdsarray of string
conflictsarray of Conflict
projectStartAtstring (date-time)
projectFinishAtstring (date-time)
statsobject
stats.taskCountinteger
stats.recomputedCountintegerTasks the scheduling engine recalculated.
stats.savedCountintegerTasks whose stored values changed.
stats.durationMsinteger
{
  "tasks": [
    {
      "id": "4dbd104a-00cd-4035-8955-255f52ea586a",
      "number": 1,
      "key": "WHX-1",
      "parentId": null,
      "sortKey": "i",
      "name": "Foundations",
      "type": "task",
      "durationMin": 1440,
      "workMin": 0,
      "progressPct": 0,
      "constraintKind": "asap",
      "constraintDate": null,
      "deadline": null,
      "calendarId": null,
      "costFixed": 0,
      "notes": null,
      "color": null,
      "wbs": null,
      "externalId": "crm-4711",
      "startAt": "2026-10-05T06:00:00.000Z",
      "finishAt": "2026-10-07T15:00:00.000Z",
      "isCritical": true,
      "totalFloatMin": 0,
      "earlyStartAt": "2026-10-05T06:00:00.000Z",
      "lateFinishAt": "2026-10-07T15:00:00.000Z",
      "actualStartAt": null,
      "actualFinishAt": null,
      "actualDurationMin": null,
      "remainingDurationMin": null,
      "manuallyScheduled": false,
      "levelingDelayMin": 0,
      "taskType": "fixed_units",
      "effortDriven": false,
      "durationOptimisticMin": null,
      "durationPessimisticMin": null
    }
  ],
  "deletedTaskIds": [],
  "dependencies": [
    {
      "id": "9ba5faf8-923d-4ecc-86f0-f245c5d179db",
      "predecessorId": "4dbd104a-00cd-4035-8955-255f52ea586a",
      "successorId": "a22fd799-c4dc-4869-a466-0dd6e96f71ce",
      "type": "FS",
      "lagMin": 0
    }
  ],
  "deletedDependencyIds": [],
  "conflicts": [],
  "projectStartAt": "2026-10-05T06:00:00.000Z",
  "projectFinishAt": "2026-10-07T15:00:00.000Z",
  "stats": {
    "taskCount": 12,
    "recomputedCount": 3,
    "savedCount": 2,
    "durationMs": 39
  }
}

People

Project members and task assignees.

Member object

FieldTypeRequiredDescription
userIdstring (uuid)
namestring
emailstring
rolestringEffective role on the project. One of: owner, editor, viewer.
directRolestring, nullableRole granted on the project itself (null = only through the workspace). One of: owner, editor, viewer.
inheritedbooleanAccess comes from the workspace.
resourceIdstring (uuid), nullableResource used when the person is assigned to tasks.
addedAtstring (date-time)
avatarUrlstring, nullable
{
  "userId": "b1666432-132a-4ea9-b1ae-76ea69e226c5",
  "name": "Anna Rossi",
  "email": "anna@example.com",
  "role": "owner",
  "directRole": null,
  "inherited": false,
  "resourceId": "0c6a8f52-3d7e-4b19-9a4e-6f1d2c8b7e30",
  "addedAt": "2026-10-01T08:30:12.418Z",
  "avatarUrl": null
}

Assignee object

FieldTypeRequiredDescription
resourceIdstring (uuid)
userIdstring (uuid), nullable
namestring
unitsPctinteger
{
  "resourceId": "0c6a8f52-3d7e-4b19-9a4e-6f1d2c8b7e30",
  "userId": "b1666432-132a-4ea9-b1ae-76ea69e226c5",
  "name": "Anna Rossi",
  "unitsPct": 100
}

Replace the people assigned to a task

PUT /api/projects/{projectId}/tasks/{taskId}/assignees (scope: write)

userIds must be members of the project (GET /members). An empty list removes everyone. Optional resourceIds replaces the people imported from a file (resources without an account) assigned to the task; omit it to leave them as they are.

Path parameters:

FieldTypeRequiredDescription
projectIdstring (uuid)yesProject id.
taskIdstring (uuid)yesTask id.

Request body:

FieldTypeRequiredDescription
userIdsarray of stringyes
resourceIdsarray of string
curl -X PUT https://planitude.app/api/projects/40be00e0-7e34-4f7a-864b-5cc986f575b8/tasks/4dbd104a-00cd-4035-8955-255f52ea586a/assignees \
  -H "Authorization: Bearer $PLANITUDE_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"userIds":["b1666432-132a-4ea9-b1ae-76ea69e226c5"]}'

Returns 200 OK: Assignees saved

FieldTypeRequiredDescription
taskIdstring (uuid)
assignmentsarray of object
assignments.idstring (uuid)
assignments.taskIdstring (uuid)
assignments.resourceIdstring (uuid)
assignments.unitsPctinteger
assignments.workMininteger
resourcesarray of object
resources.idstring (uuid)
resources.userIdstring (uuid), nullable
resources.namestring
resources.kindstringOne of: work, material, cost.
resources.emailstring, nullable
resources.costPerHournumber
resources.costPerUsenumber
resources.capacityPctinteger
resources.calendarIdstring (uuid), nullable
resources.colorstring, nullable
resources.avatarUrlstring, nullable
needsRescheduleboolean
conflictsarray of Conflict
{
  "taskId": "4dbd104a-00cd-4035-8955-255f52ea586a",
  "assignments": [
    {
      "id": "e62ac96b-e68e-40d3-842e-335e27dfd099",
      "taskId": "4dbd104a-00cd-4035-8955-255f52ea586a",
      "resourceId": "0c6a8f52-3d7e-4b19-9a4e-6f1d2c8b7e30",
      "unitsPct": 100,
      "workMin": 0
    }
  ],
  "resources": [
    {
      "id": "0c6a8f52-3d7e-4b19-9a4e-6f1d2c8b7e30",
      "userId": "b1666432-132a-4ea9-b1ae-76ea69e226c5",
      "name": "Anna Rossi",
      "kind": "builtin",
      "email": "anna@example.com",
      "costPerHour": 0,
      "costPerUse": 0,
      "capacityPct": 0,
      "calendarId": null,
      "color": null,
      "avatarUrl": null
    }
  ],
  "needsReschedule": false,
  "conflicts": []
}

People with access to the project (assignable)

GET /api/projects/{projectId}/members (scope: read)

Path parameters:

FieldTypeRequiredDescription
projectIdstring (uuid)yesProject id.
curl https://planitude.app/api/projects/40be00e0-7e34-4f7a-864b-5cc986f575b8/members \
  -H "Authorization: Bearer $PLANITUDE_API_KEY"

Returns 200 OK: Members

FieldTypeRequiredDescription
membersarray of Member
rolestring
{
  "members": [
    {
      "userId": "b1666432-132a-4ea9-b1ae-76ea69e226c5",
      "name": "Anna Rossi",
      "email": "anna@example.com",
      "role": "owner",
      "directRole": null,
      "inherited": false,
      "resourceId": "0c6a8f52-3d7e-4b19-9a4e-6f1d2c8b7e30",
      "addedAt": "2026-10-01T08:30:12.418Z",
      "avatarUrl": null
    }
  ],
  "role": "owner"
}

Webhooks

Outgoing webhook subscriptions (REST hooks: subscribe/unsubscribe).

Webhook object

FieldTypeRequiredDescription
idstring (uuid)
workspaceIdstring (uuid)
workspaceNamestring
projectIdstring (uuid), nullablenull = every project of the workspace.
projectNamestring, nullable
namestring
urlstring (uri)
eventsarray of stringOne of: project.created, project.updated, project.deleted, task.created, task.updated, task.deleted, task.completed, task.dates_changed, dependency.created, dependency.deleted, baseline.saved, member.invited, member.joined, comment.created.
secretPrefixstringFirst characters of the signing secret (not secret).
activeboolean
disabledReasonstring, nullableOne of: failures, gone, project_deleted, user.
disabledAtstring (date-time), nullable
consecutiveFailuresinteger
viaApibooleanCreated with the API (REST hook) rather than from the profile.
createdByobject, nullable
createdBy.idstring (uuid)
createdBy.namestring, nullable
createdAtstring (date-time)
updatedAtstring (date-time)
lastDeliveryAtstring (date-time), nullable
lastSuccessAtstring (date-time), nullable
{
  "id": "1747ee7f-a395-4d3c-94b0-54b513d71d88",
  "workspaceId": "824650d2-d0b3-4cac-8df8-5743ae9ff65a",
  "workspaceName": "Acme Construction",
  "projectId": "40be00e0-7e34-4f7a-864b-5cc986f575b8",
  "projectName": "Warehouse expansion",
  "name": "Warehouse sync",
  "url": "https://example.com/hooks/planitude",
  "events": [
    "task.created",
    "task.completed"
  ],
  "secretPrefix": "whsec_Fdwl",
  "active": true,
  "disabledReason": null,
  "disabledAt": null,
  "consecutiveFailures": 0,
  "viaApi": true,
  "createdBy": {
    "id": "b1666432-132a-4ea9-b1ae-76ea69e226c5",
    "name": "Anna Rossi"
  },
  "createdAt": "2026-10-01T08:30:12.418Z",
  "updatedAt": "2026-10-01T08:30:12.418Z",
  "lastDeliveryAt": "2026-10-01T08:30:12.418Z",
  "lastSuccessAt": "2026-10-01T08:30:12.418Z"
}

Delivery object

FieldTypeRequiredDescription
idstring (uuid)
eventIdstring (uuid)
eventTypestring
statusstringOne of: pending, success, failed, cancelled.
attemptsinteger
nextAttemptAtstring (date-time), nullable
lastAttemptAtstring (date-time), nullable
responseStatusinteger, nullable
responseExcerptstring, nullable
errorstring, nullable
durationMsinteger, nullable
manualboolean
createdAtstring (date-time)
{
  "id": "4b207161-80f9-40bb-bc3f-d8f70315be00",
  "eventId": "6f1c1d1e-2f4b-4d0a-9a77-2a3c5b1e9f10",
  "eventType": "task.completed",
  "status": "success",
  "attempts": 1,
  "nextAttemptAt": null,
  "lastAttemptAt": "2026-10-01T08:30:12.418Z",
  "responseStatus": 200,
  "responseExcerpt": "{\"ok\":true}",
  "error": null,
  "durationMs": 39,
  "manual": false,
  "createdAt": "2026-10-01T08:30:12.418Z"
}

Event object

FieldTypeRequiredDescription
idstring (uuid)yesEvent id, also in the Planitude-Event-Id header: use it to deduplicate.
typestringyesOne of: project.created, project.updated, project.deleted, task.created, task.updated, task.deleted, task.completed, task.dates_changed, dependency.created, dependency.deleted, baseline.saved, member.invited, member.joined, comment.created, webhook.ping.
apiVersionstringyesOne of: 2026-10-01.
createdAtstring (date-time)yes
workspaceIdstring (uuid)yes
projectIdstring (uuid), nullable
actorobject, nullableWho made the change (null for system changes).
actor.idstring (uuid)
actor.namestring, nullable
dataobjectyes
{
  "id": "6f1c1d1e-2f4b-4d0a-9a77-2a3c5b1e9f10",
  "type": "project.created",
  "apiVersion": "2026-10-01",
  "createdAt": "2026-10-01T08:30:12.418Z",
  "workspaceId": "824650d2-d0b3-4cac-8df8-5743ae9ff65a",
  "projectId": "40be00e0-7e34-4f7a-864b-5cc986f575b8",
  "actor": {
    "id": "b1666432-132a-4ea9-b1ae-76ea69e226c5",
    "name": "Anna Rossi"
  },
  "data": {}
}

Project (event payload) object

Compact project representation used in webhook payloads.

FieldTypeRequiredDescription
idstring (uuid)
workspaceIdstring (uuid)
keystring
namestring
descriptionstring, nullable
startDatestring (date)
deadlinestring (date), nullable
currencystring
scheduleFromstringOne of: forward, backward.
colorstring, nullable
archivedboolean
createdAtstring (date-time)
updatedAtstring (date-time)
urlstring (uri)
{
  "id": "40be00e0-7e34-4f7a-864b-5cc986f575b8",
  "workspaceId": "824650d2-d0b3-4cac-8df8-5743ae9ff65a",
  "key": "WHX",
  "name": "Warehouse expansion",
  "description": null,
  "startDate": "2026-10-05",
  "deadline": null,
  "currency": "EUR",
  "scheduleFrom": "forward",
  "color": null,
  "archived": false,
  "createdAt": "2026-10-01T08:30:12.418Z",
  "updatedAt": "2026-10-01T08:30:12.418Z",
  "url": "https://planitude.app/app/projects/40be00e0-7e34-4f7a-864b-5cc986f575b8"
}

List webhook subscriptions

GET /api/webhooks (scope: read)

Query parameters:

FieldTypeRequiredDescription
workspaceIdstring (uuid)
projectIdstring (uuid)
curl https://planitude.app/api/webhooks \
  -H "Authorization: Bearer $PLANITUDE_API_KEY"

Returns 200 OK: Subscriptions

FieldTypeRequiredDescription
webhooksarray of Webhook
{
  "webhooks": [
    {
      "id": "1747ee7f-a395-4d3c-94b0-54b513d71d88",
      "workspaceId": "824650d2-d0b3-4cac-8df8-5743ae9ff65a",
      "workspaceName": "Acme Construction",
      "projectId": "40be00e0-7e34-4f7a-864b-5cc986f575b8",
      "projectName": "Warehouse expansion",
      "name": "Warehouse sync",
      "url": "https://example.com/hooks/planitude",
      "events": [
        "task.created",
        "task.completed"
      ],
      "secretPrefix": "whsec_Fdwl",
      "active": true,
      "disabledReason": null,
      "disabledAt": null,
      "consecutiveFailures": 0,
      "viaApi": true,
      "createdBy": {
        "id": "b1666432-132a-4ea9-b1ae-76ea69e226c5",
        "name": "Anna Rossi"
      },
      "createdAt": "2026-10-01T08:30:12.418Z",
      "updatedAt": "2026-10-01T08:30:12.418Z",
      "lastDeliveryAt": "2026-10-01T08:30:12.418Z",
      "lastSuccessAt": "2026-10-01T08:30:12.418Z"
    }
  ]
}

Subscribe (REST hook)

POST /api/webhooks (scope: write)

Creates a subscription. The response contains the signing secret: it is shown only once. Aliases accepted for automation platforms: targetUrl for url, event for a single event.

Request body:

FieldTypeRequiredDescription
urlstring (uri)yesHTTPS endpoint that receives the events. Public addresses only.
eventsarray of stringyes* = all events. One of: project.created, project.updated, project.deleted, task.created, task.updated, task.deleted, task.completed, task.dates_changed, dependency.created, dependency.deleted, baseline.saved, member.invited, member.joined, comment.created, *.
namestring
projectIdstring (uuid)Limit to one project (editor role needed). Omit for the whole workspace (owner/admin).
workspaceIdstring (uuid)
curl -X POST https://planitude.app/api/webhooks \
  -H "Authorization: Bearer $PLANITUDE_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "url": "https://example.com/hooks/planitude",
  "events": [
    "task.created",
    "task.completed"
  ],
  "projectId": "40be00e0-7e34-4f7a-864b-5cc986f575b8"
}'

Returns 201 Created: Created

FieldTypeRequiredDescription
webhookWebhook
secretstringwhsec_...
{
  "webhook": {
    "id": "1747ee7f-a395-4d3c-94b0-54b513d71d88",
    "workspaceId": "824650d2-d0b3-4cac-8df8-5743ae9ff65a",
    "workspaceName": "Acme Construction",
    "projectId": "40be00e0-7e34-4f7a-864b-5cc986f575b8",
    "projectName": "Warehouse expansion",
    "name": "Warehouse sync",
    "url": "https://example.com/hooks/planitude",
    "events": [
      "task.created",
      "task.completed"
    ],
    "secretPrefix": "whsec_Fdwl",
    "active": true,
    "disabledReason": null,
    "disabledAt": null,
    "consecutiveFailures": 0,
    "viaApi": true,
    "createdBy": {
      "id": "b1666432-132a-4ea9-b1ae-76ea69e226c5",
      "name": "Anna Rossi"
    },
    "createdAt": "2026-10-01T08:30:12.418Z",
    "updatedAt": "2026-10-01T08:30:12.418Z",
    "lastDeliveryAt": "2026-10-01T08:30:12.418Z",
    "lastSuccessAt": "2026-10-01T08:30:12.418Z"
  },
  "secret": "whsec_3dvU0T1oaYlW4kH6m2Vb8nqXcR9eZpLsYw1QhGk"
}

Event catalog (public)

GET /api/webhooks/events (public, no key)

curl https://planitude.app/api/webhooks/events

Returns 200 OK: Events

FieldTypeRequiredDescription
apiVersionstring
eventsarray of string
{
  "apiVersion": "2026-10-01",
  "events": [
    "project.created",
    "project.updated",
    "project.deleted",
    "task.created",
    "task.updated",
    "task.deleted",
    "task.completed",
    "task.dates_changed",
    "dependency.created",
    "dependency.deleted",
    "baseline.saved",
    "member.invited",
    "member.joined",
    "comment.created"
  ]
}

Get a subscription

GET /api/webhooks/{webhookId} (scope: read)

Path parameters:

FieldTypeRequiredDescription
webhookIdstring (uuid)yesWebhook id.
curl https://planitude.app/api/webhooks/1747ee7f-a395-4d3c-94b0-54b513d71d88 \
  -H "Authorization: Bearer $PLANITUDE_API_KEY"

Returns 200 OK: Subscription

FieldTypeRequiredDescription
webhookWebhook
{
  "webhook": {
    "id": "1747ee7f-a395-4d3c-94b0-54b513d71d88",
    "workspaceId": "824650d2-d0b3-4cac-8df8-5743ae9ff65a",
    "workspaceName": "Acme Construction",
    "projectId": "40be00e0-7e34-4f7a-864b-5cc986f575b8",
    "projectName": "Warehouse expansion",
    "name": "Warehouse sync",
    "url": "https://example.com/hooks/planitude",
    "events": [
      "task.created",
      "task.completed"
    ],
    "secretPrefix": "whsec_Fdwl",
    "active": true,
    "disabledReason": null,
    "disabledAt": null,
    "consecutiveFailures": 0,
    "viaApi": true,
    "createdBy": {
      "id": "b1666432-132a-4ea9-b1ae-76ea69e226c5",
      "name": "Anna Rossi"
    },
    "createdAt": "2026-10-01T08:30:12.418Z",
    "updatedAt": "2026-10-01T08:30:12.418Z",
    "lastDeliveryAt": "2026-10-01T08:30:12.418Z",
    "lastSuccessAt": "2026-10-01T08:30:12.418Z"
  }
}

Update, pause or resume a subscription

PATCH /api/webhooks/{webhookId} (scope: write)

Path parameters:

FieldTypeRequiredDescription
webhookIdstring (uuid)yesWebhook id.

Request body:

FieldTypeRequiredDescription
namestring
urlstring (uri)
eventsarray of string
activebooleanfalse = pause; true = resume (also resets the failure counter).
curl -X PATCH https://planitude.app/api/webhooks/1747ee7f-a395-4d3c-94b0-54b513d71d88 \
  -H "Authorization: Bearer $PLANITUDE_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"active":false}'

Returns 200 OK: Updated

FieldTypeRequiredDescription
webhookWebhook
{
  "webhook": {
    "id": "1747ee7f-a395-4d3c-94b0-54b513d71d88",
    "workspaceId": "824650d2-d0b3-4cac-8df8-5743ae9ff65a",
    "workspaceName": "Acme Construction",
    "projectId": "40be00e0-7e34-4f7a-864b-5cc986f575b8",
    "projectName": "Warehouse expansion",
    "name": "Warehouse sync",
    "url": "https://example.com/hooks/planitude",
    "events": [
      "task.created",
      "task.completed"
    ],
    "secretPrefix": "whsec_Fdwl",
    "active": true,
    "disabledReason": null,
    "disabledAt": null,
    "consecutiveFailures": 0,
    "viaApi": true,
    "createdBy": {
      "id": "b1666432-132a-4ea9-b1ae-76ea69e226c5",
      "name": "Anna Rossi"
    },
    "createdAt": "2026-10-01T08:30:12.418Z",
    "updatedAt": "2026-10-01T08:30:12.418Z",
    "lastDeliveryAt": "2026-10-01T08:30:12.418Z",
    "lastSuccessAt": "2026-10-01T08:30:12.418Z"
  }
}

Unsubscribe (REST hook)

DELETE /api/webhooks/{webhookId} (scope: write)

Path parameters:

FieldTypeRequiredDescription
webhookIdstring (uuid)yesWebhook id.
curl -X DELETE https://planitude.app/api/webhooks/1747ee7f-a395-4d3c-94b0-54b513d71d88 \
  -H "Authorization: Bearer $PLANITUDE_API_KEY"

Returns 204 No Content: Deleted

Delivery log (newest first, kept 30 days)

GET /api/webhooks/{webhookId}/deliveries (scope: read)

Path parameters:

FieldTypeRequiredDescription
webhookIdstring (uuid)yesWebhook id.

Query parameters:

FieldTypeRequiredDescription
limitinteger1-100. Default: 50.
beforestring (date-time)nextCursor of the previous page.
curl https://planitude.app/api/webhooks/1747ee7f-a395-4d3c-94b0-54b513d71d88/deliveries \
  -H "Authorization: Bearer $PLANITUDE_API_KEY"

Returns 200 OK: Deliveries

FieldTypeRequiredDescription
deliveriesarray of Delivery
nextCursorstring, nullable
{
  "deliveries": [
    {
      "id": "4b207161-80f9-40bb-bc3f-d8f70315be00",
      "eventId": "6f1c1d1e-2f4b-4d0a-9a77-2a3c5b1e9f10",
      "eventType": "task.completed",
      "status": "success",
      "attempts": 1,
      "nextAttemptAt": null,
      "lastAttemptAt": "2026-10-01T08:30:12.418Z",
      "responseStatus": 200,
      "responseExcerpt": "{\"ok\":true}",
      "error": null,
      "durationMs": 39,
      "manual": false,
      "createdAt": "2026-10-01T08:30:12.418Z"
    }
  ],
  "nextCursor": null
}

Body sent with a delivery

GET /api/webhooks/{webhookId}/deliveries/{deliveryId} (scope: read)

Path parameters:

FieldTypeRequiredDescription
webhookIdstring (uuid)yesWebhook id.
deliveryIdstring (uuid)yes
curl https://planitude.app/api/webhooks/1747ee7f-a395-4d3c-94b0-54b513d71d88/deliveries/4b207161-80f9-40bb-bc3f-d8f70315be00 \
  -H "Authorization: Bearer $PLANITUDE_API_KEY"

Returns 200 OK: Payload

FieldTypeRequiredDescription
payloadEvent
{
  "payload": {
    "id": "6f1c1d1e-2f4b-4d0a-9a77-2a3c5b1e9f10",
    "type": "project.created",
    "apiVersion": "2026-10-01",
    "createdAt": "2026-10-01T08:30:12.418Z",
    "workspaceId": "824650d2-d0b3-4cac-8df8-5743ae9ff65a",
    "projectId": "40be00e0-7e34-4f7a-864b-5cc986f575b8",
    "actor": {
      "id": "b1666432-132a-4ea9-b1ae-76ea69e226c5",
      "name": "Anna Rossi"
    },
    "data": {}
  }
}

Send the same event again

POST /api/webhooks/{webhookId}/deliveries/{deliveryId}/redeliver (scope: write)

Path parameters:

FieldTypeRequiredDescription
webhookIdstring (uuid)yesWebhook id.
deliveryIdstring (uuid)yes
curl -X POST https://planitude.app/api/webhooks/1747ee7f-a395-4d3c-94b0-54b513d71d88/deliveries/4b207161-80f9-40bb-bc3f-d8f70315be00/redeliver \
  -H "Authorization: Bearer $PLANITUDE_API_KEY"

Returns 202 Accepted: Queued

FieldTypeRequiredDescription
deliveryDelivery
{
  "delivery": {
    "id": "4b207161-80f9-40bb-bc3f-d8f70315be00",
    "eventId": "6f1c1d1e-2f4b-4d0a-9a77-2a3c5b1e9f10",
    "eventType": "task.completed",
    "status": "success",
    "attempts": 1,
    "nextAttemptAt": null,
    "lastAttemptAt": "2026-10-01T08:30:12.418Z",
    "responseStatus": 200,
    "responseExcerpt": "{\"ok\":true}",
    "error": null,
    "durationMs": 39,
    "manual": false,
    "createdAt": "2026-10-01T08:30:12.418Z"
  }
}

Send a webhook.ping test event

POST /api/webhooks/{webhookId}/test (scope: write)

Path parameters:

FieldTypeRequiredDescription
webhookIdstring (uuid)yesWebhook id.
curl -X POST https://planitude.app/api/webhooks/1747ee7f-a395-4d3c-94b0-54b513d71d88/test \
  -H "Authorization: Bearer $PLANITUDE_API_KEY"

Returns 202 Accepted: Queued

FieldTypeRequiredDescription
deliveryDelivery
{
  "delivery": {
    "id": "4b207161-80f9-40bb-bc3f-d8f70315be00",
    "eventId": "6f1c1d1e-2f4b-4d0a-9a77-2a3c5b1e9f10",
    "eventType": "task.completed",
    "status": "success",
    "attempts": 1,
    "nextAttemptAt": null,
    "lastAttemptAt": "2026-10-01T08:30:12.418Z",
    "responseStatus": 200,
    "responseExcerpt": "{\"ok\":true}",
    "error": null,
    "durationMs": 39,
    "manual": false,
    "createdAt": "2026-10-01T08:30:12.418Z"
  }
}

Generate a new signing secret

POST /api/webhooks/{webhookId}/rotate-secret (scope: write)

Path parameters:

FieldTypeRequiredDescription
webhookIdstring (uuid)yesWebhook id.
curl -X POST https://planitude.app/api/webhooks/1747ee7f-a395-4d3c-94b0-54b513d71d88/rotate-secret \
  -H "Authorization: Bearer $PLANITUDE_API_KEY"

Returns 200 OK: New secret (shown once)

FieldTypeRequiredDescription
webhookWebhook
secretstring
{
  "webhook": {
    "id": "1747ee7f-a395-4d3c-94b0-54b513d71d88",
    "workspaceId": "824650d2-d0b3-4cac-8df8-5743ae9ff65a",
    "workspaceName": "Acme Construction",
    "projectId": "40be00e0-7e34-4f7a-864b-5cc986f575b8",
    "projectName": "Warehouse expansion",
    "name": "Warehouse sync",
    "url": "https://example.com/hooks/planitude",
    "events": [
      "task.created",
      "task.completed"
    ],
    "secretPrefix": "whsec_Fdwl",
    "active": true,
    "disabledReason": null,
    "disabledAt": null,
    "consecutiveFailures": 0,
    "viaApi": true,
    "createdBy": {
      "id": "b1666432-132a-4ea9-b1ae-76ea69e226c5",
      "name": "Anna Rossi"
    },
    "createdAt": "2026-10-01T08:30:12.418Z",
    "updatedAt": "2026-10-01T08:30:12.418Z",
    "lastDeliveryAt": "2026-10-01T08:30:12.418Z",
    "lastSuccessAt": "2026-10-01T08:30:12.418Z"
  },
  "secret": "whsec_3dvU0T1oaYlW4kH6m2Vb8nqXcR9eZpLsYw1QhGk"
}

Webhooks

Webhooks send an HTTPS POST with a JSON body to your endpoint as soon as something changes, whether the change comes from the web app, the API, the MCP server or an import. A webhook covers one project, or every project of a workspace (owners and admins).

Create them in Profile → Webhooks or with the API, as REST hooks: subscribe with POST /api/webhooks, unsubscribe with DELETE. The signing secret is shown only once.

HeaderDescription
Planitude-Signaturet=<unix seconds>,v1=<hex HMAC-SHA256>: see Verify signatures.
Planitude-EventEvent type, e.g. task.completed.
Planitude-Event-IdEvent id, the same in every retry: use it to deduplicate.
Planitude-DeliveryId of this delivery (changes with a manual resend).
User-AgentPlanitude-Webhooks/1.0 (+https://planitude.app/developers)
Content-Typeapplication/json

Event types

Subscribe to single events, or to * for all of them. Every event has the same envelope; data changes with the type.

EventDescription
project.createdA project was created (blank, from a template, duplicated or imported from MS Project).
project.updatedProject fields changed (name, dates, deadline, archive...), or a saved version was restored.
project.deletedA project was deleted.
task.createdA task was created.
task.updatedTask fields or assignees changed. changes lists only the fields that changed.
task.deletedA task was deleted (one event per task of a deleted branch).
task.completedTask progress reached 100%.
task.dates_changedStart or finish moved, because of an edit (cause: edit) or because the schedule was recalculated (cause: reschedule).
dependency.createdA dependency was created or changed.
dependency.deletedA dependency was removed.
baseline.savedA baseline was saved.
member.invitedAn invite link was created for the project.
member.joinedA person joined the project.
comment.createdA comment was added to a task.

project.created

{
  "id": "6f1c1d1e-2f4b-4d0a-9a77-2a3c5b1e9f10",
  "type": "project.created",
  "apiVersion": "2026-10-01",
  "createdAt": "2026-10-01T08:30:12.418Z",
  "workspaceId": "824650d2-d0b3-4cac-8df8-5743ae9ff65a",
  "projectId": "40be00e0-7e34-4f7a-864b-5cc986f575b8",
  "actor": {
    "id": "b1666432-132a-4ea9-b1ae-76ea69e226c5",
    "name": "Anna Rossi"
  },
  "data": {
    "project": {
      "id": "40be00e0-7e34-4f7a-864b-5cc986f575b8",
      "workspaceId": "824650d2-d0b3-4cac-8df8-5743ae9ff65a",
      "key": "WHX",
      "name": "Warehouse expansion",
      "description": null,
      "startDate": "2026-10-05",
      "deadline": null,
      "currency": "EUR",
      "scheduleFrom": "forward",
      "color": null,
      "archived": false,
      "createdAt": "2026-10-01T08:30:12.418Z",
      "updatedAt": "2026-10-01T08:30:12.418Z",
      "url": "https://planitude.app/app/projects/40be00e0-7e34-4f7a-864b-5cc986f575b8"
    },
    "source": "blank"
  }
}

project.updated

{
  "id": "6f1c1d1e-2f4b-4d0a-9a77-2a3c5b1e9f10",
  "type": "project.updated",
  "apiVersion": "2026-10-01",
  "createdAt": "2026-10-01T08:30:12.418Z",
  "workspaceId": "824650d2-d0b3-4cac-8df8-5743ae9ff65a",
  "projectId": "40be00e0-7e34-4f7a-864b-5cc986f575b8",
  "actor": {
    "id": "b1666432-132a-4ea9-b1ae-76ea69e226c5",
    "name": "Anna Rossi"
  },
  "data": {
    "project": {
      "id": "40be00e0-7e34-4f7a-864b-5cc986f575b8",
      "workspaceId": "824650d2-d0b3-4cac-8df8-5743ae9ff65a",
      "key": "WHX",
      "name": "Warehouse expansion",
      "description": null,
      "startDate": "2026-10-05",
      "deadline": null,
      "currency": "EUR",
      "scheduleFrom": "forward",
      "color": null,
      "archived": false,
      "createdAt": "2026-10-01T08:30:12.418Z",
      "updatedAt": "2026-10-01T08:30:12.418Z",
      "url": "https://planitude.app/app/projects/40be00e0-7e34-4f7a-864b-5cc986f575b8"
    },
    "changes": {
      "deadline": {
        "from": null,
        "to": "2026-12-18"
      }
    },
    "reason": "version_restored"
  }
}

project.deleted

{
  "id": "6f1c1d1e-2f4b-4d0a-9a77-2a3c5b1e9f10",
  "type": "project.deleted",
  "apiVersion": "2026-10-01",
  "createdAt": "2026-10-01T08:30:12.418Z",
  "workspaceId": "824650d2-d0b3-4cac-8df8-5743ae9ff65a",
  "projectId": "40be00e0-7e34-4f7a-864b-5cc986f575b8",
  "actor": {
    "id": "b1666432-132a-4ea9-b1ae-76ea69e226c5",
    "name": "Anna Rossi"
  },
  "data": {
    "project": {
      "id": "40be00e0-7e34-4f7a-864b-5cc986f575b8",
      "workspaceId": "824650d2-d0b3-4cac-8df8-5743ae9ff65a",
      "key": "WHX",
      "name": "Warehouse expansion",
      "description": null,
      "startDate": "2026-10-05",
      "deadline": null,
      "currency": "EUR",
      "scheduleFrom": "forward",
      "color": null,
      "archived": false,
      "createdAt": "2026-10-01T08:30:12.418Z",
      "updatedAt": "2026-10-01T08:30:12.418Z",
      "url": "https://planitude.app/app/projects/40be00e0-7e34-4f7a-864b-5cc986f575b8"
    }
  }
}

task.created

{
  "id": "6f1c1d1e-2f4b-4d0a-9a77-2a3c5b1e9f10",
  "type": "task.created",
  "apiVersion": "2026-10-01",
  "createdAt": "2026-10-01T08:30:12.418Z",
  "workspaceId": "824650d2-d0b3-4cac-8df8-5743ae9ff65a",
  "projectId": "40be00e0-7e34-4f7a-864b-5cc986f575b8",
  "actor": {
    "id": "b1666432-132a-4ea9-b1ae-76ea69e226c5",
    "name": "Anna Rossi"
  },
  "data": {
    "task": {
      "id": "4dbd104a-00cd-4035-8955-255f52ea586a",
      "number": 1,
      "key": "WHX-1",
      "parentId": null,
      "name": "Foundations",
      "type": "task",
      "durationMin": 1440,
      "workMin": 0,
      "progressPct": 0,
      "constraintKind": "asap",
      "constraintDate": null,
      "deadline": null,
      "calendarId": null,
      "costFixed": 0,
      "notes": null,
      "color": null,
      "wbs": null,
      "externalId": "crm-4711",
      "startAt": "2026-10-05T06:00:00.000Z",
      "finishAt": "2026-10-07T15:00:00.000Z",
      "isCritical": true,
      "totalFloatMin": 0,
      "earlyStartAt": "2026-10-05T06:00:00.000Z",
      "lateFinishAt": "2026-10-07T15:00:00.000Z",
      "actualStartAt": null,
      "actualFinishAt": null,
      "actualDurationMin": null,
      "remainingDurationMin": null,
      "manuallyScheduled": false,
      "levelingDelayMin": 0,
      "taskType": "fixed_units",
      "effortDriven": false,
      "durationOptimisticMin": null,
      "durationPessimisticMin": null,
      "projectId": "40be00e0-7e34-4f7a-864b-5cc986f575b8",
      "completed": false,
      "assignees": [
        {
          "resourceId": "0c6a8f52-3d7e-4b19-9a4e-6f1d2c8b7e30",
          "userId": "b1666432-132a-4ea9-b1ae-76ea69e226c5",
          "name": "Anna Rossi",
          "unitsPct": 100
        }
      ],
      "url": "https://planitude.app/app/t/WHX-1"
    }
  }
}

task.updated

{
  "id": "6f1c1d1e-2f4b-4d0a-9a77-2a3c5b1e9f10",
  "type": "task.updated",
  "apiVersion": "2026-10-01",
  "createdAt": "2026-10-01T08:30:12.418Z",
  "workspaceId": "824650d2-d0b3-4cac-8df8-5743ae9ff65a",
  "projectId": "40be00e0-7e34-4f7a-864b-5cc986f575b8",
  "actor": {
    "id": "b1666432-132a-4ea9-b1ae-76ea69e226c5",
    "name": "Anna Rossi"
  },
  "data": {
    "task": {
      "id": "4dbd104a-00cd-4035-8955-255f52ea586a",
      "number": 1,
      "key": "WHX-1",
      "parentId": null,
      "name": "Foundations",
      "type": "task",
      "durationMin": 1440,
      "workMin": 0,
      "progressPct": 0,
      "constraintKind": "asap",
      "constraintDate": null,
      "deadline": null,
      "calendarId": null,
      "costFixed": 0,
      "notes": null,
      "color": null,
      "wbs": null,
      "externalId": "crm-4711",
      "startAt": "2026-10-05T06:00:00.000Z",
      "finishAt": "2026-10-07T15:00:00.000Z",
      "isCritical": true,
      "totalFloatMin": 0,
      "earlyStartAt": "2026-10-05T06:00:00.000Z",
      "lateFinishAt": "2026-10-07T15:00:00.000Z",
      "actualStartAt": null,
      "actualFinishAt": null,
      "actualDurationMin": null,
      "remainingDurationMin": null,
      "manuallyScheduled": false,
      "levelingDelayMin": 0,
      "taskType": "fixed_units",
      "effortDriven": false,
      "durationOptimisticMin": null,
      "durationPessimisticMin": null,
      "projectId": "40be00e0-7e34-4f7a-864b-5cc986f575b8",
      "completed": false,
      "assignees": [
        {
          "resourceId": "0c6a8f52-3d7e-4b19-9a4e-6f1d2c8b7e30",
          "userId": "b1666432-132a-4ea9-b1ae-76ea69e226c5",
          "name": "Anna Rossi",
          "unitsPct": 100
        }
      ],
      "url": "https://planitude.app/app/t/WHX-1"
    },
    "changes": {
      "progressPct": {
        "from": 40,
        "to": 60
      }
    }
  }
}

task.deleted

{
  "id": "6f1c1d1e-2f4b-4d0a-9a77-2a3c5b1e9f10",
  "type": "task.deleted",
  "apiVersion": "2026-10-01",
  "createdAt": "2026-10-01T08:30:12.418Z",
  "workspaceId": "824650d2-d0b3-4cac-8df8-5743ae9ff65a",
  "projectId": "40be00e0-7e34-4f7a-864b-5cc986f575b8",
  "actor": {
    "id": "b1666432-132a-4ea9-b1ae-76ea69e226c5",
    "name": "Anna Rossi"
  },
  "data": {
    "task": {
      "id": "4dbd104a-00cd-4035-8955-255f52ea586a",
      "number": 1,
      "key": "WHX-1",
      "parentId": null,
      "name": "Foundations",
      "type": "task",
      "durationMin": 1440,
      "workMin": 0,
      "progressPct": 0,
      "constraintKind": "asap",
      "constraintDate": null,
      "deadline": null,
      "calendarId": null,
      "costFixed": 0,
      "notes": null,
      "color": null,
      "wbs": null,
      "externalId": "crm-4711",
      "startAt": "2026-10-05T06:00:00.000Z",
      "finishAt": "2026-10-07T15:00:00.000Z",
      "isCritical": true,
      "totalFloatMin": 0,
      "earlyStartAt": "2026-10-05T06:00:00.000Z",
      "lateFinishAt": "2026-10-07T15:00:00.000Z",
      "actualStartAt": null,
      "actualFinishAt": null,
      "actualDurationMin": null,
      "remainingDurationMin": null,
      "manuallyScheduled": false,
      "levelingDelayMin": 0,
      "taskType": "fixed_units",
      "effortDriven": false,
      "durationOptimisticMin": null,
      "durationPessimisticMin": null,
      "projectId": "40be00e0-7e34-4f7a-864b-5cc986f575b8",
      "completed": false,
      "assignees": [
        {
          "resourceId": "0c6a8f52-3d7e-4b19-9a4e-6f1d2c8b7e30",
          "userId": "b1666432-132a-4ea9-b1ae-76ea69e226c5",
          "name": "Anna Rossi",
          "unitsPct": 100
        }
      ],
      "url": "https://planitude.app/app/t/WHX-1"
    }
  }
}

task.completed

{
  "id": "6f1c1d1e-2f4b-4d0a-9a77-2a3c5b1e9f10",
  "type": "task.completed",
  "apiVersion": "2026-10-01",
  "createdAt": "2026-10-01T08:30:12.418Z",
  "workspaceId": "824650d2-d0b3-4cac-8df8-5743ae9ff65a",
  "projectId": "40be00e0-7e34-4f7a-864b-5cc986f575b8",
  "actor": {
    "id": "b1666432-132a-4ea9-b1ae-76ea69e226c5",
    "name": "Anna Rossi"
  },
  "data": {
    "task": {
      "id": "4dbd104a-00cd-4035-8955-255f52ea586a",
      "number": 1,
      "key": "WHX-1",
      "parentId": null,
      "name": "Foundations",
      "type": "task",
      "durationMin": 1440,
      "workMin": 0,
      "progressPct": 100,
      "constraintKind": "asap",
      "constraintDate": null,
      "deadline": null,
      "calendarId": null,
      "costFixed": 0,
      "notes": null,
      "color": null,
      "wbs": null,
      "externalId": "crm-4711",
      "startAt": "2026-10-05T06:00:00.000Z",
      "finishAt": "2026-10-07T15:00:00.000Z",
      "isCritical": true,
      "totalFloatMin": 0,
      "earlyStartAt": "2026-10-05T06:00:00.000Z",
      "lateFinishAt": "2026-10-07T15:00:00.000Z",
      "actualStartAt": null,
      "actualFinishAt": null,
      "actualDurationMin": null,
      "remainingDurationMin": null,
      "manuallyScheduled": false,
      "levelingDelayMin": 0,
      "taskType": "fixed_units",
      "effortDriven": false,
      "durationOptimisticMin": null,
      "durationPessimisticMin": null,
      "projectId": "40be00e0-7e34-4f7a-864b-5cc986f575b8",
      "completed": true,
      "assignees": [
        {
          "resourceId": "0c6a8f52-3d7e-4b19-9a4e-6f1d2c8b7e30",
          "userId": "b1666432-132a-4ea9-b1ae-76ea69e226c5",
          "name": "Anna Rossi",
          "unitsPct": 100
        }
      ],
      "url": "https://planitude.app/app/t/WHX-1"
    }
  }
}

task.dates_changed

{
  "id": "6f1c1d1e-2f4b-4d0a-9a77-2a3c5b1e9f10",
  "type": "task.dates_changed",
  "apiVersion": "2026-10-01",
  "createdAt": "2026-10-01T08:30:12.418Z",
  "workspaceId": "824650d2-d0b3-4cac-8df8-5743ae9ff65a",
  "projectId": "40be00e0-7e34-4f7a-864b-5cc986f575b8",
  "actor": {
    "id": "b1666432-132a-4ea9-b1ae-76ea69e226c5",
    "name": "Anna Rossi"
  },
  "data": {
    "task": {
      "id": "4dbd104a-00cd-4035-8955-255f52ea586a",
      "number": 1,
      "key": "WHX-1",
      "parentId": null,
      "name": "Foundations",
      "type": "task",
      "durationMin": 1440,
      "workMin": 0,
      "progressPct": 0,
      "constraintKind": "asap",
      "constraintDate": null,
      "deadline": null,
      "calendarId": null,
      "costFixed": 0,
      "notes": null,
      "color": null,
      "wbs": null,
      "externalId": "crm-4711",
      "startAt": "2026-10-05T06:00:00.000Z",
      "finishAt": "2026-10-07T15:00:00.000Z",
      "isCritical": true,
      "totalFloatMin": 0,
      "earlyStartAt": "2026-10-05T06:00:00.000Z",
      "lateFinishAt": "2026-10-07T15:00:00.000Z",
      "actualStartAt": null,
      "actualFinishAt": null,
      "actualDurationMin": null,
      "remainingDurationMin": null,
      "manuallyScheduled": false,
      "levelingDelayMin": 0,
      "taskType": "fixed_units",
      "effortDriven": false,
      "durationOptimisticMin": null,
      "durationPessimisticMin": null,
      "projectId": "40be00e0-7e34-4f7a-864b-5cc986f575b8",
      "completed": false,
      "assignees": [
        {
          "resourceId": "0c6a8f52-3d7e-4b19-9a4e-6f1d2c8b7e30",
          "userId": "b1666432-132a-4ea9-b1ae-76ea69e226c5",
          "name": "Anna Rossi",
          "unitsPct": 100
        }
      ],
      "url": "https://planitude.app/app/t/WHX-1"
    },
    "cause": "reschedule",
    "changes": {
      "startAt": {
        "from": "2026-10-02T06:00:00.000Z",
        "to": "2026-10-05T06:00:00.000Z"
      },
      "finishAt": {
        "from": "2026-10-06T15:00:00.000Z",
        "to": "2026-10-07T15:00:00.000Z"
      }
    }
  }
}

dependency.created

{
  "id": "6f1c1d1e-2f4b-4d0a-9a77-2a3c5b1e9f10",
  "type": "dependency.created",
  "apiVersion": "2026-10-01",
  "createdAt": "2026-10-01T08:30:12.418Z",
  "workspaceId": "824650d2-d0b3-4cac-8df8-5743ae9ff65a",
  "projectId": "40be00e0-7e34-4f7a-864b-5cc986f575b8",
  "actor": {
    "id": "b1666432-132a-4ea9-b1ae-76ea69e226c5",
    "name": "Anna Rossi"
  },
  "data": {
    "dependency": {
      "id": "9ba5faf8-923d-4ecc-86f0-f245c5d179db",
      "predecessorId": "4dbd104a-00cd-4035-8955-255f52ea586a",
      "successorId": "a22fd799-c4dc-4869-a466-0dd6e96f71ce",
      "type": "FS",
      "lagMin": 0
    }
  }
}

dependency.deleted

{
  "id": "6f1c1d1e-2f4b-4d0a-9a77-2a3c5b1e9f10",
  "type": "dependency.deleted",
  "apiVersion": "2026-10-01",
  "createdAt": "2026-10-01T08:30:12.418Z",
  "workspaceId": "824650d2-d0b3-4cac-8df8-5743ae9ff65a",
  "projectId": "40be00e0-7e34-4f7a-864b-5cc986f575b8",
  "actor": {
    "id": "b1666432-132a-4ea9-b1ae-76ea69e226c5",
    "name": "Anna Rossi"
  },
  "data": {
    "dependency": {
      "id": "9ba5faf8-923d-4ecc-86f0-f245c5d179db",
      "predecessorId": "4dbd104a-00cd-4035-8955-255f52ea586a",
      "successorId": "a22fd799-c4dc-4869-a466-0dd6e96f71ce",
      "type": "FS",
      "lagMin": 0
    }
  }
}

baseline.saved

{
  "id": "6f1c1d1e-2f4b-4d0a-9a77-2a3c5b1e9f10",
  "type": "baseline.saved",
  "apiVersion": "2026-10-01",
  "createdAt": "2026-10-01T08:30:12.418Z",
  "workspaceId": "824650d2-d0b3-4cac-8df8-5743ae9ff65a",
  "projectId": "40be00e0-7e34-4f7a-864b-5cc986f575b8",
  "actor": {
    "id": "b1666432-132a-4ea9-b1ae-76ea69e226c5",
    "name": "Anna Rossi"
  },
  "data": {
    "baseline": {
      "id": "b5d68d05-0640-48f9-8eeb-ed7804f4a116",
      "name": "Plan v1",
      "index": null,
      "capturedAt": "2026-10-01T08:30:12.418Z",
      "capturedBy": "b1666432-132a-4ea9-b1ae-76ea69e226c5",
      "taskCount": 12
    }
  }
}

member.invited

{
  "id": "6f1c1d1e-2f4b-4d0a-9a77-2a3c5b1e9f10",
  "type": "member.invited",
  "apiVersion": "2026-10-01",
  "createdAt": "2026-10-01T08:30:12.418Z",
  "workspaceId": "824650d2-d0b3-4cac-8df8-5743ae9ff65a",
  "projectId": "40be00e0-7e34-4f7a-864b-5cc986f575b8",
  "actor": {
    "id": "b1666432-132a-4ea9-b1ae-76ea69e226c5",
    "name": "Anna Rossi"
  },
  "data": {
    "invite": {
      "id": "5d0b7c11-8e2a-4f63-a1d4-93c7e2b6f804",
      "role": "editor",
      "expiresAt": "2026-10-31T08:30:12.418Z",
      "maxUses": null
    }
  }
}

member.joined

{
  "id": "6f1c1d1e-2f4b-4d0a-9a77-2a3c5b1e9f10",
  "type": "member.joined",
  "apiVersion": "2026-10-01",
  "createdAt": "2026-10-01T08:30:12.418Z",
  "workspaceId": "824650d2-d0b3-4cac-8df8-5743ae9ff65a",
  "projectId": "40be00e0-7e34-4f7a-864b-5cc986f575b8",
  "actor": {
    "id": "b1666432-132a-4ea9-b1ae-76ea69e226c5",
    "name": "Anna Rossi"
  },
  "data": {
    "member": {
      "userId": "b1666432-132a-4ea9-b1ae-76ea69e226c5",
      "name": "Anna Rossi",
      "role": "editor"
    }
  }
}

comment.created

{
  "id": "6f1c1d1e-2f4b-4d0a-9a77-2a3c5b1e9f10",
  "type": "comment.created",
  "apiVersion": "2026-10-01",
  "createdAt": "2026-10-01T08:30:12.418Z",
  "workspaceId": "824650d2-d0b3-4cac-8df8-5743ae9ff65a",
  "projectId": "40be00e0-7e34-4f7a-864b-5cc986f575b8",
  "actor": {
    "id": "b1666432-132a-4ea9-b1ae-76ea69e226c5",
    "name": "Anna Rossi"
  },
  "data": {
    "comment": {
      "id": "6f1c1d1e-2f4b-4d0a-9a77-2a3c5b1e9f10",
      "taskId": "4dbd104a-00cd-4035-8955-255f52ea586a",
      "body": "Concrete delivered, we can start the walls.",
      "author": {
        "id": "b1666432-132a-4ea9-b1ae-76ea69e226c5",
        "name": "Anna Rossi"
      },
      "createdAt": "2026-10-01T08:30:12.418Z"
    },
    "task": {
      "id": "4dbd104a-00cd-4035-8955-255f52ea586a",
      "number": 1,
      "key": "WHX-1",
      "parentId": null,
      "name": "Foundations",
      "type": "task",
      "durationMin": 1440,
      "workMin": 0,
      "progressPct": 0,
      "constraintKind": "asap",
      "constraintDate": null,
      "deadline": null,
      "calendarId": null,
      "costFixed": 0,
      "notes": null,
      "color": null,
      "wbs": null,
      "externalId": "crm-4711",
      "startAt": "2026-10-05T06:00:00.000Z",
      "finishAt": "2026-10-07T15:00:00.000Z",
      "isCritical": true,
      "totalFloatMin": 0,
      "earlyStartAt": "2026-10-05T06:00:00.000Z",
      "lateFinishAt": "2026-10-07T15:00:00.000Z",
      "actualStartAt": null,
      "actualFinishAt": null,
      "actualDurationMin": null,
      "remainingDurationMin": null,
      "manuallyScheduled": false,
      "levelingDelayMin": 0,
      "taskType": "fixed_units",
      "effortDriven": false,
      "durationOptimisticMin": null,
      "durationPessimisticMin": null,
      "projectId": "40be00e0-7e34-4f7a-864b-5cc986f575b8",
      "completed": false,
      "assignees": [
        {
          "resourceId": "0c6a8f52-3d7e-4b19-9a4e-6f1d2c8b7e30",
          "userId": "b1666432-132a-4ea9-b1ae-76ea69e226c5",
          "name": "Anna Rossi",
          "unitsPct": 100
        }
      ],
      "url": "https://planitude.app/app/t/WHX-1"
    }
  }
}

Verify signatures

Every request is signed with HMAC-SHA256 and your webhook’s secret. The Planitude-Signature header holds a timestamp t and the signature v1, computed on t, a dot and the raw body.

  1. Use the raw body, before any JSON parsing: a re-serialized body has a different signature.
  2. Compute the HMAC and compare it in constant time.
  3. Reject requests whose timestamp is more than 5 minutes from your clock: a captured request cannot be replayed.
  4. Rotate the secret whenever you want: the old one stops working at once.
import { createHmac, timingSafeEqual } from "node:crypto";

// rawBody: the request body as a string, before JSON.parse
export function verifyPlanitude(rawBody, header, secret, toleranceSec = 300) {
  const parts = Object.fromEntries(header.split(",").map((p) => p.split("=")));
  const t = Number(parts.t);
  if (!t || Math.abs(Date.now() / 1000 - t) > toleranceSec) return false; // replay
  const expected = createHmac("sha256", secret).update(`${t}.${rawBody}`).digest();
  const given = Buffer.from(parts.v1 ?? "", "hex");
  return given.length === expected.length && timingSafeEqual(given, expected);
}

// Express: app.post("/hooks/planitude", express.raw({ type: "application/json" }), (req, res) => {
//   if (!verifyPlanitude(req.body.toString("utf8"), req.get("Planitude-Signature"), process.env.PLANITUDE_WEBHOOK_SECRET)) return res.sendStatus(400);
//   res.sendStatus(200); // then process the event in a queue
// });
import hashlib
import hmac
import time

def verify_planitude(raw_body: bytes, header: str, secret: str, tolerance: int = 300) -> bool:
    parts = dict(p.split("=", 1) for p in header.split(","))
    t = int(parts.get("t", "0"))
    if abs(time.time() - t) > tolerance:
        return False  # replay
    expected = hmac.new(secret.encode(), f"{t}.".encode() + raw_body, hashlib.sha256).hexdigest()
    return hmac.compare_digest(expected, parts.get("v1", ""))

# Flask: verify_planitude(request.get_data(), request.headers["Planitude-Signature"], os.environ["PLANITUDE_WEBHOOK_SECRET"])

Retries and delivery

Answer with any 2xx within 10 seconds and do the real work afterwards, in a queue. Redirects are not followed. On errors and timeouts Planitude tries up to 6 times in total: immediately, then after 1 min, 5 min, 30 min, 2 h, 12 h.

Delivery is at least once and not ordered. Store the event id (Planitude-Event-Id, also id in the body) and skip the ones you have already processed; order by createdAt.

After 5 events in a row fail every attempt, the webhook is disabled and its owner receives an email. A 410 Gone answer disables it at once. Turn it back on from the profile, or with PATCH and active: true.

The delivery log (status, HTTP code, response excerpt, payload, manual resend) is kept for 30 days. Only public addresses are accepted as destinations.

Test your endpoint

  1. Send a webhook.ping event with the test endpoint, or with the Test button in the profile.
  2. To receive events on your computer, expose your local server with a tunnel (for example ngrok or Cloudflare Tunnel) and subscribe its public HTTPS address.
  3. Open the delivery log to see status, HTTP code and response, and resend a delivery once your endpoint is fixed.
curl -X POST https://planitude.app/api/webhooks/1747ee7f-a395-4d3c-94b0-54b513d71d88/test \
  -H "Authorization: Bearer $PLANITUDE_API_KEY"

MCP server

Planitude is an MCP (Model Context Protocol) server: AI assistants and code editors read projects, analyze the critical path and the workload, and create or change tasks, with your permissions. Web apps connect with OAuth (you sign in and allow access); command-line tools and editors use an API key.

Server URL: https://planitude.app/mcp

ChatGPT (OAuth)

In ChatGPT open Settings › Security and login and turn on Developer mode. Create an app with the server URL and OAuth authentication, then sign in to Planitude and allow access.

Server URL:

https://planitude.app/mcp

Official documentation: https://developers.openai.com/api/docs/guides/developer-mode

Claude (OAuth)

Open Customize › Connectors › Add custom connector, paste the server URL and choose Connect: Planitude opens, you sign in and allow access. It works in claude.ai, Claude Desktop and the mobile app.

Server URL:

https://planitude.app/mcp

Official documentation: https://claude.com/docs/connectors/custom/add-unlisted

Gemini (OAuth)

On gemini.google.com open Settings › Connected Apps, choose Add a custom app and paste the server URL; then sign in to Planitude and allow access.

Server URL:

https://planitude.app/mcp

Official documentation: https://support.google.com/gemini/answer/17209137

Claude Code (API key)

Run the command with your key in an environment variable. Add --scope user to use it in every project and check with claude mcp list.

Terminal:

claude mcp add --transport http planitude https://planitude.app/mcp \
  --header "Authorization: Bearer $PLANITUDE_API_KEY"

Official documentation: https://code.claude.com/docs/en/mcp

Codex (API key)

Put the key in PLANITUDE_API_KEY and add the block to ~/.codex/config.toml: it applies to the CLI, the app and the IDE extension.

~/.codex/config.toml:

[mcp_servers.planitude]
url = "https://planitude.app/mcp"
bearer_token_env_var = "PLANITUDE_API_KEY"

Official documentation: https://learn.chatgpt.com/docs/extend/mcp

Cursor (API key)

Add the block to ~/.cursor/mcp.json (all projects) or .cursor/mcp.json (one project); Cursor reads the key from the environment.

~/.cursor/mcp.json:

{
  "mcpServers": {
    "planitude": {
      "url": "https://planitude.app/mcp",
      "headers": {
        "Authorization": "Bearer ${env:PLANITUDE_API_KEY}"
      }
    }
  }
}

Official documentation: https://cursor.com/docs/context/mcp

Visual Studio Code (API key)

Add .vscode/mcp.json to the workspace: VS Code asks for the key once and stores it securely, outside the file.

.vscode/mcp.json:

{
  "servers": {
    "planitude": {
      "type": "http",
      "url": "https://planitude.app/mcp",
      "headers": {
        "Authorization": "Bearer ${input:planitude-key}"
      }
    }
  },
  "inputs": [
    {
      "id": "planitude-key",
      "type": "promptString",
      "description": "Planitude API key",
      "password": true
    }
  ]
}

Official documentation: https://code.visualstudio.com/docs/copilot/customization/mcp-servers

Gemini CLI (API key)

Run the command with your key in an environment variable, or add the block to ~/.gemini/settings.json.

Terminal:

gemini mcp add --transport http \
  --header "Authorization: Bearer $PLANITUDE_API_KEY" planitude https://planitude.app/mcp

~/.gemini/settings.json:

{
  "mcpServers": {
    "planitude": {
      "httpUrl": "https://planitude.app/mcp",
      "headers": {
        "Authorization": "Bearer $PLANITUDE_API_KEY"
      }
    }
  }
}

Official documentation: https://geminicli.com/docs/tools/mcp-server/

Tools

The server exposes 29 tools, from reading and analysis to safe changes. None of them deletes projects or tasks.

  • read: planitude_list_projects, planitude_portfolio_overview, planitude_get_project, planitude_critical_path, planitude_delay_impact, planitude_monte_carlo, planitude_workload, planitude_list_baselines, planitude_list_versions, planitude_list_templates, planitude_list_comments, planitude_preview_import
  • write: planitude_create_project, planitude_create_project_from_template, planitude_create_project_from_plan, planitude_import_file, planitude_edit_project, planitude_add_task, planitude_update_task, planitude_set_progress, planitude_indent_task, planitude_outdent_task, planitude_link_tasks, planitude_unlink_tasks, planitude_reschedule, planitude_configure_simulation, planitude_add_comment, planitude_save_baseline, planitude_save_version

Step-by-step guide for ChatGPT, Claude and Gemini

AI agents

Add up to 5 external AI agents to the workspace as members: Claude Code, Cursor, Codex, Gemini CLI, GitHub Copilot or any MCP client. Assign them tasks, mention them in comments and review their changes.

Owners and admins create agent keys in Agents, Connection tab. A key starts with pln_, is shown once and stored as a hash; each agent can have up to 2 keys, and revoking one takes effect at once.

Agent keys work only with the MCP server, which then exposes the agent tools instead of the usual ones. On the REST API they get 403 agent_credential.

claude mcp add --transport http planitude https://planitude.app/mcp \
  --header "Authorization: Bearer $PLANITUDE_AGENT_KEY"

Each key either reads and comments or can edit. Claiming, completing and proposing changes need a key that can edit; otherwise the answer is 403 agent_key_read_only.

An agent sees only the projects it was added to, as viewer (reads and comments) or editor (also changes tasks), never as owner. The project also needs “Allow agents in this project”: it is off by default and the owner turns it on.

Agents can change a task’s name, notes, duration, progress and deadline, or create tasks. No tool deletes, invites people or changes roles.

Default limits per agent: 60 requests a minute and 200 writes a day, adjustable up to 240 and 2,000. At most 200 changes per project can wait for review.

Read tools put text written by people (names, notes, comments) inside planitude-data blocks with a random nonce, as JSON strings, and mark rows flagged by an automatic check as SUSPICIOUS: it is data, never instructions. Comments read with a personal key get the same blocks.

How AI agents work in Planitude

Agent tools

With an agent key, the MCP server exposes these 16 tools:

  • read: planitude_list_my_projects, planitude_list_tasks, planitude_read_comments, planitude_list_my_proposals
  • propose: planitude_add_comment, planitude_propose_task_update, planitude_propose_status, planitude_propose_task_create
  • events: planitude_poll_events, planitude_ack_events
  • runs: planitude_list_my_runs, planitude_claim_task, planitude_extend_lease, planitude_complete_task, planitude_fail_task, planitude_release_task
{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "tools/call",
  "params": {
    "name": "planitude_claim_task",
    "arguments": {
      "projectId": "40be00e0-7e34-4f7a-864b-5cc986f575b8",
      "task": "4dbd104a-00cd-4035-8955-255f52ea586a",
      "leaseMinutes": 30
    }
  }
}

During an Autonomous run, the agent’s changes to its own tasks apply right away and stay in the history, ready to restore. Otherwise, and in With approval mode, they wait in “Needs review” until someone on the project applies them.

Without a webhook, the agent reads its events with planitude_poll_events (up to 50 at a time) and confirms them with planitude_ack_events.

Run webhook

To start an agent on its own, turn on execution in Agents, Execution tab, and enter your runner’s webhook: Planitude sends a signed POST when the agent has a task to run. The secret starts with whsec_ and is shown once. The URL must be a public HTTPS address: private and local ones are refused.

Signature, headers, the 10-second timeout and retries (1 min, 5 min, 30 min, 2 h, 12 h) work as for the other webhooks. After 5 failed deliveries in a row the webhook turns off: save the URL again to turn it back on.

The notice carries only ids, never task text: the agent reads the task over MCP.

EventDescription
task.assignedOnly with the “on assignment” trigger: a person assigned the task to the agent. Once per assignment.
task.start_dueDefault trigger: the task’s start date has come (a working day of its calendar).
task.due_todayThe task is due today (deadline or planned finish). Opens a run only if the task never had one.
task.overdueThe task is past its due date, once per due date. Opens a run only if the task never had one.
run.retryThe lease expired without completion, or a person chose Rerun: a new attempt is queued.
run.cancelA run in progress was cancelled (by a person, an emergency stop or the rules): stop working on it.
webhook.pingSent by “Send test event” in the agent’s Execution tab.
{
  "id": "6f1c1d1e-2f4b-4d0a-9a77-2a3c5b1e9f10",
  "type": "task.start_due",
  "apiVersion": "2026-10-01",
  "createdAt": "2026-10-14T07:00:00.000Z",
  "workspaceId": "824650d2-d0b3-4cac-8df8-5743ae9ff65a",
  "projectId": "40be00e0-7e34-4f7a-864b-5cc986f575b8",
  "actor": null,
  "data": {
    "agentId": "b4d17e2c-93a8-4c6f-8e15-6a0f2d9c7b31",
    "taskId": "4dbd104a-00cd-4035-8955-255f52ea586a",
    "runId": "7c3e2a91-5d4b-4f08-b6e1-2f9a0c8d4e57",
    "newRun": true,
    "attempt": 1,
    "leaseMinutes": 30,
    "writeMode": "autonomous",
    "day": "2026-10-14",
    "links": {
      "mcp": "https://planitude.app/mcp",
      "app": "https://planitude.app/app/projects/40be00e0-7e34-4f7a-864b-5cc986f575b8?task=4dbd104a-00cd-4035-8955-255f52ea586a"
    }
  }
}

A notice with newRun: true opens a run. The agent claims it with planitude_claim_task and holds a lease of 30 minutes (from 5 to 240), renewed with planitude_extend_lease. If the lease expires, a new attempt starts, up to 3 (at most 10). A run nobody claims expires after 24 hours.

planitude_complete_task sets the task to 100% and posts the report as a comment; with open subtasks it answers 409 subtasks_open. planitude_fail_task records why and notifies the team, who can rerun it; planitude_release_task gives the task back with no retry.

Each run allows 50 edits (up to 500, then 429) and each agent 5 open runs (then 409 too_many_open_runs). Autonomous, the default mode, applies changes at once and lets you restore them; With approval sends them to review. An emergency stop, per project or for the whole workspace, cancels open runs and blocks writes.

Write a runner

The runner is your small service that receives the notices and starts the agent. Any language works; in Node 20+ it needs no dependencies.

  1. Verify the signature on the raw body and answer 200 at once: Planitude waits 10 seconds at most.
  2. Skip events you have already seen (Planitude-Event-Id): retries resend the same event.
  3. On newRun: true, claim the task and start the agent, for example claude -p with the Planitude MCP server and the agent key.
  4. Renew the lease while the agent works; on run.cancel, stop it.
  5. Ask the agent to end with PLANITUDE: DONE only when the whole task is done, then complete with its report. Otherwise (PLANITUDE: NOT DONE - … or no last line) fail with what is left.

Treat task text as data: tell the agent never to follow instructions found in it, and allow it only the Planitude tools it needs.

// after verifyPlanitude(rawBody, signature, secret) and dedupe on Planitude-Event-Id
res.sendStatus(200); // answer at once, work afterwards
const { type, projectId, data } = event;
if (type === "run.cancel") return stopAgent(data.runId);
if (!data.newRun) return;

await callTool("planitude_claim_task", { projectId, task: data.taskId, leaseMinutes: data.leaseMinutes });
const out = await runAgent(data); // renew with planitude_extend_lease while it works
if (out.lastLine === "PLANITUDE: DONE") {
  await callTool("planitude_complete_task", { runId: data.runId, report: out.report }); // task -> 100%
} else {
  await callTool("planitude_fail_task", { runId: data.runId, reason: out.whatIsLeft });
}

// callTool = JSON-RPC tools/call to the MCP URL
// with Authorization: Bearer <agent key>
# mcp.json: {"mcpServers":{"planitude":{"type":"http","url":"https://planitude.app/mcp",
#            "headers":{"Authorization":"Bearer pln_..."}}}}
claude -p --output-format text \
  --mcp-config mcp.json --allowedTools mcp__planitude < prompt.txt

OData feed (Power BI, Excel)

A read-only OData v4 feed for Power BI, Excel and any OData client, at two addresses: one project, or every project the key owner can see in a workspace.

Authenticate with a personal API key with read access: Authorization: Bearer, or HTTP Basic with the key as password (any user name), which is the method Power BI and Excel offer. OAuth tokens and session cookies are not accepted. A key limited to one workspace cannot read the others.

Tables: Projects, Tasks, Milestones, Dependencies, Resources, Assignments. The schema is at $metadata (CSDL XML) and the service document at the root address.

Supported options: $select, $filter (comparisons, and/or/not, in, contains, startswith, endswith, tolower, toupper), $orderby, $top, $skip, $count and /$count. Pages hold 2000 rows by default, up to 5000 with Prefer: odata.maxpagesize; follow @odata.nextLink. Responses carry an ETag: send If-None-Match to get 304. $expand, $apply and $search are not supported (400).

A feed covers at most 500 projects and 25000 tasks (beyond that, 413). Requests count toward the read limit of the key, 240 per minute.

curl "https://planitude.app/api/odata/projects/40be00e0-7e34-4f7a-864b-5cc986f575b8/Tasks?$select=Key,Name,Start,Finish,ProgressPct,IsCritical&$filter=IsCritical&$orderby=Start&$top=2&$count=true" \
  -u "powerbi:$PLANITUDE_API_KEY"

# schema (CSDL XML), for Power BI, Excel and any OData client
curl "https://planitude.app/api/odata/projects/40be00e0-7e34-4f7a-864b-5cc986f575b8/$metadata" -u "powerbi:$PLANITUDE_API_KEY"
{
  "@odata.context": "https://planitude.app/api/odata/projects/40be00e0-7e34-4f7a-864b-5cc986f575b8/$metadata#Tasks(Key,Name,Start,Finish,ProgressPct,IsCritical)",
  "@odata.count": 3,
  "value": [
    {
      "Key": "WHX-2",
      "Name": "Foundations",
      "Start": "2026-10-05T07:00:00Z",
      "Finish": "2026-10-06T15:00:00Z",
      "ProgressPct": 100,
      "IsCritical": true
    },
    {
      "Key": "WHX-3",
      "Name": "Walls",
      "Start": "2026-10-07T07:00:00Z",
      "Finish": "2026-10-09T15:00:00Z",
      "ProgressPct": 50,
      "IsCritical": true
    }
  ]
}

Step-by-step guide for Power BI and Excel

Ready-made integrations

No code needed: these integrations use the same API and webhooks and are set up in a few clicks.

  • Jira (Available): Two-way sync of tasks, dates and dependencies with Jira issues.
  • Azure DevOps (Available): Two-way sync with Azure DevOps Boards work items.
  • Slack (Available): Project updates, daily summaries and a slash command in your channels.
  • Microsoft Teams (Available): Updates, summaries and a Planitude tab in Microsoft Teams.
  • Power BI, Excel (Available): Read-only OData feed with projects, tasks, milestones and assignments for reports and dashboards.
  • Zapier, Make, n8n (Coming soon): Instant triggers and actions on tasks and projects for no-code automations.

Changelog

  • 2026-10-15: New read-only OData v4 feed at /api/odata/projects/:projectId and /api/odata/workspaces/:workspaceId for Power BI, Excel and other OData clients: six tables, $metadata, $select, $filter, $orderby, $top, $skip, $count, server-driven paging and ETag.
  • 2026-10-15: New endpoint GET /api/projects/:projectId/export: the plan as Microsoft Project XML, CSV, PNG image or PDF, with options for scale, date range, critical path, baseline, dependencies, paper size and orientation.
  • 2026-10-15: All API keys now start with pln_. Keys that start with gnt_ no longer work: create a new one.
  • 2026-10-14: Agents run tasks: signed run webhook (task.assigned, task.start_due, task.due_today, task.overdue, run.retry, run.cancel), 6 run tools with a lease and a guide to writing a runner.
  • 2026-10-14: MCP read tools wrap text written by people in planitude-data blocks with a nonce, as JSON strings with a SUSPICIOUS mark, for every kind of key.
  • 2026-10-14: AI agents as workspace members: agent keys that work only with the MCP server, 10 agent tools to read, comment and propose changes, and an event queue.
  • 2026-10-02: New developer documentation, generated from the OpenAPI document. The OpenAPI document now describes every field returned for members, comments, baselines, templates and assignees.
  • 2026-10-01: API version 2026-10-01: signed outgoing webhooks, documented public REST API (OpenAPI 3.1), cursor pagination and search by externalId.
  • 2026-09-30: OAuth 2.1 for the MCP server and the API: ChatGPT, Claude and Gemini connect without copying a key.
  • 2026-09-28: MCP server and personal API keys with read or read + write access.