Planitude
Se connecterS’inscrire

Développeurs

API, webhooks et intégrations

Lisez et modifiez projets, tâches et dépendances depuis votre propre code, et recevez un webhook signé dès qu'un changement a lieu. La même API alimente nos intégrations Zapier, Make et n8n.

La référence technique ci-dessous est en anglais.

Démarrage rapide

Create a personal API key in Profile → API keys (read-only or read + write), then call the REST API with it as a bearer token. Base URL: https://planitude.app.

List your projects
curl https://planitude.app/api/projects \
  -H "Authorization: Bearer $PLANITUDE_KEY"
Create a task (dates are computed by the scheduling engine)
curl -X POST https://planitude.app/api/projects/$PROJECT_ID/tasks \
  -H "Authorization: Bearer $PLANITUDE_KEY" \
  -H "Content-Type: application/json" \
  -d '{"name": "Foundations", "durationMin": 1440, "externalId": "crm-4711"}'
Find it again by your own id
curl "https://planitude.app/api/projects/$PROJECT_ID/tasks?externalId=crm-4711" \
  -H "Authorization: Bearer $PLANITUDE_KEY"

Durations and lags are in working minutes (an 8-hour day is 480). Instants are ISO 8601 in UTC. Errors are JSON: {"error": "<code>", "details": {...}}.

Authentification

API keys (gnt_…) are personal: they act as you, with your roles on each project, and never more. Choose read or read + write; you can restrict a key to one workspace and give it an expiry. Every call is logged and visible next to the key.

OAuth 2.1 is available for apps that connect on behalf of users (this is what the Zapier app uses): authorization code with PKCE (S256), refresh tokens rotated on every use. Pass resource=https://planitude.app/api to the authorize endpoint to get a token for the REST API; scopes are planitude:read and planitude:write. Clients register with POST /api/oauth/register (RFC 7591). Endpoints: https://planitude.app/oauth/authorize, https://planitude.app/api/oauth/token, https://planitude.app/api/oauth/revoke; metadata at https://planitude.app/.well-known/oauth-authorization-server.

For safety, keys and OAuth apps cannot delete projects or tasks: deleting stays in the web app, where a person confirms it.

Limites de débit

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 (seconds). Other limits: 50 webhooks per workspace, 1,000 changes per change set.

Tâches, recherche et pagination

GET /api/projects/{id}/tasks is paginated with a cursor: pass limit (1-500, default 100) and the nextCursor of the previous page as cursor. Filters: externalId (exact), q (name contains), updatedSince, completed=true|false; order with sort=number (default), -number (newest first) or -updatedAt.

Write with POST /api/projects/{id}/tasks (one task, or a change set applied in a single transaction), PATCH /api/projects/{id}/tasks/{taskId}, PUT …/tasks/{taskId}/assignees with project members from GET …/members, and POST …/dependencies (FS, SS, FF, SF, with lag; a dependency that would create a cycle is refused with 409). New projects: POST /api/projects or POST /api/projects/from-template with a template from GET /api/templates.

Webhooks

Webhooks send an HTTPS POST with a JSON body to your endpoint whenever something changes, whether the change comes from the web app, the API, the MCP server or an import. Create them in Profile → Webhooks or with the API (REST hooks: subscribe with POST /api/webhooks, unsubscribe with DELETE /api/webhooks/{id}). A webhook covers one project, or every project of a workspace (owners and admins only).

Subscribe
curl -X POST https://planitude.app/api/webhooks \
  -H "Authorization: Bearer $PLANITUDE_KEY" \
  -H "Content-Type: application/json" \
  -d '{"url": "https://example.com/hooks/planitude", "events": ["task.created", "task.completed"], "projectId": "'$PROJECT_ID'"}'
# -> 201 {"webhook": {...}, "secret": "whsec_..."}   (the secret is shown only once)
EventWhen
project.createdA project was created: blank, from a template, duplicated or imported (data.source).
project.updatedProject fields changed, or a saved version was restored (data.changes, data.reason).
project.deletedA project was deleted.
task.createdA task was created.
task.updatedTask fields or assignees changed (data.changes has only what changed).
task.deletedA task was deleted (one event per task when a branch is deleted).
task.completedTask progress reached 100%.
task.dates_changedStart or finish moved: by an edit (cause: edit) or by the recalculation (cause: reschedule).
dependency.createdA dependency was created or its type/lag changed.
dependency.deletedA dependency was removed.
baseline.savedA baseline was saved (manually or automatically at the first progress).
member.invitedAn invite link was created for the project.
member.joinedA person joined the project.
comment.createdA comment was added to a task.

Every request carries Planitude-Event, Planitude-Event-Id, Planitude-Delivery and Planitude-Signature. The body is versioned (apiVersion: "2026-10-01"): fields may be added, never removed or retyped without a new version.

Payload (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",
      "key": "WHD-1",
      "number": 1,
      "projectId": "40be00e0-7e34-4f7a-864b-5cc986f575b8",
      "parentId": null,
      "name": "Excavation",
      "type": "task",
      "startAt": "2026-10-05T06:00:00.000Z",
      "finishAt": "2026-10-08T15:00:00.000Z",
      "durationMin": 1920,
      "progressPct": 100,
      "completed": true,
      "isCritical": true,
      "externalId": "ext-001",
      "assignees": [
        {
          "resourceId": "0c6a…",
          "userId": "b166…",
          "name": "Anna Rossi",
          "unitsPct": 100
        }
      ],
      "url": "https://planitude.app/app/t/WHD-1",
      "…": "all other task fields"
    }
  }
}

Vérifier les signatures

The header looks like Planitude-Signature: t=1727771412,v1=5257a869…. v1 is the hex HMAC-SHA256 of <t>.<raw body> with your signing secret. Compute it on the raw body (before parsing JSON), compare in constant time, and reject requests whose t is more than 5 minutes away from your clock: a captured request cannot be replayed later.

Node.js
import { createHmac, timingSafeEqual } from "node:crypto";

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);
}
Python
import hashlib, hmac, 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", ""))

Rotate the secret any time from the profile or with POST /api/webhooks/{id}/rotate-secret; the old one stops working immediately.

Nouvelles tentatives et livraison

Answer with any 2xx within 10 seconds. Redirects are not followed. On errors and timeouts Planitude retries up to 6 times in total: immediately, then after 1 min, 5 min, 30 min, 2 h, 12 h. After 5 events in a row fail every attempt, the webhook is disabled and its owner receives an email; turn it back on from the profile. A 410 Gone answer disables it at once (REST hook convention).

Delivery is at least once and not ordered: use the event id (Planitude-Event-Id) to ignore duplicates and createdAt to order. The delivery log (status, HTTP code, response excerpt, payload, manual resend) is kept for 30 days. Only public addresses are accepted as destinations: private, loopback, link-local and cloud-metadata addresses are refused, also after DNS resolution.

Zapier, Make et n8n

Zapier: triggers New Task, Task Completed, Task Dates Changed and New Project (instant, with REST hooks), actions Create Task, Update Task, Create Project from Template and Find Task. Make: custom app with the same modules and instant triggers. n8n: community node n8n-nodes-planitude with a trigger node and task/project operations. All three use this API and these webhooks: anything they do, you can do with a script.

Référence de l'API

The complete reference (every endpoint, field and webhook payload) is the OpenAPI 3.1 document at /api/openapi.json: import it into Postman, Insomnia or a code generator. The event catalog is also available at /api/webhooks/events.

La publicité permet à Planitude de rester gratuit