---
title: "Planitude API, webhooks og MCP for utviklere"
description: "REST API med OpenAPI 3.1, signerte webhooks og en MCP-server for AI-assistenter: bygg videre på Planitude, det gratis nettbaserte verktøyet for prosjektstyring."
url: https://planitude.app/no/developers
language: nb
---

# Bygg med Planitude

Et REST API, signerte webhooks og en MCP-server: hold systemene dine synkronisert med planen, reager på hver endring og la en AI-assistent jobbe med prosjektene dine.

*Kode, feltnavn og den tekniske referansen er på engelsk, som i de fleste API-dokumentasjoner.*

- OpenAPI 3.1: https://planitude.app/api/openapi.json
- Basis-URL: https://planitude.app
- MCP: https://planitude.app/mcp
- API version: 2026-10-01

- **Synkroniser oppgaver med systemene dine**: Opprett prosjekter og oppgaver fra et CRM, et ERP eller et skript, finn dem igjen med din egen id via `externalId`, og la planleggingsmotoren beregne datoene.
- **Reager på hver endring**: Motta en signert HTTPS-forespørsel når en oppgave opprettes, fullføres eller flyttes, enten endringen kommer fra appen, API-et, en import eller en AI-assistent.
- **Koble til en AI-assistent**: ChatGPT, Claude, Gemini, Cursor og VS Code leser planene dine og foreslår endringer via MCP-serveren, med dine tillatelser og ikke mer.

Forespørsler og svar er JSON over HTTPS. Varigheter og forsinkelser oppgis i **arbeidsminutter** (en arbeidsdag på 8 timer er 480), tidspunkter er ISO 8601 i UTC, datoer uten klokkeslett er `YYYY-MM-DD`.

## Hurtigstart

Fra null til din første webhook i tre steg. Alt du trenger er en terminal.

### 1. Opprett en API-nøkkel

Åpne [Profil → API-nøkler](https://planitude.app/no/app/profile/api-keys), velg **Les og skriv** og kopier nøkkelen: den begynner med `gnt_` og vises bare én gang. Lagre den i en miljøvariabel.

```bash
export PLANITUDE_API_KEY="gnt_…"
```

### 2. Gjør ditt første kall

List opp prosjektene du kan se. Nøkkelen handler som deg: de samme prosjektene, de samme rollene.

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

Forventet resultat:

```json
{
  "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. Motta en webhook

Abonner et offentlig HTTPS-endepunkt på hendelsene du trenger, send deretter en testhendelse med en gang og verifiser signaturen.

```bash
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"
}'
```

```json
{
  "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"
      ]
    }
  }
}
```

## Autentisering

Autentiser hver forespørsel med en personlig API-nøkkel i `Authorization`-headeren, som et bærertoken. Nøkler er personlige: de handler som deg, med rollen din i hvert prosjekt, aldri mer. Du kan begrense en nøkkel til ett arbeidsområde, gi den en utløpsdato og trekke den tilbake når som helst; hvert kall logges ved siden av nøkkelen.

| Omfang | Tillater |
| --- | --- |
| `read` (`planitude:read`) | Les prosjekter, oppgaver, avhengigheter, medlemmer, kommentarer, grunnlinjer og webhooks. |
| `write` (`planitude:write`) | Alt i lesetilgang, pluss opprette og endre prosjekter, oppgaver, avhengigheter, kommentarer, grunnlinjer og webhooks. |

Nøkler og OAuth-apper **kan ikke slette** prosjekter eller oppgaver: sletting skjer bare i nettappen, der en person bekrefter det. Et skript som peker på feil id kan ikke tømme en plan.

### OAuth 2.1 for tredjepartsapper

Apper som handler på vegne av andre bruker OAuth 2.1: autorisasjonskode med PKCE (S256), refresh-tokens som roteres ved hver bruk, dynamisk klientregistrering (RFC 7591). Be om ressursen `https://planitude.app/api` og scopet `planitude:read` eller `planitude:write`.

Endepunktene er publisert i standard discovery-dokumenter, så OAuth-biblioteker konfigurerer seg selv.

```json
{
  "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"
  ]
}
```

### God praksis

- Oppbevar nøkler i miljøvariabler eller en hemmelighetsbehandler, aldri i kode eller i et repositorium. Nøkler begynner med `gnt_`, så hemmelighetsskannere kan oppdage dem.
- Bruk en skrivebeskyttet nøkkel når du bare leser, og én nøkkel per integrasjon: du kan tilbakekalle én uten å stoppe de andre.
- Gi nøkler som brukes av skript en utløpsdato, og roter dem.
- Kall API-et fra serveren din, ikke fra en nettleser: en nøkkel i frontend-kode er en offentlig nøkkel.

## Feil

Planitude bruker standard HTTP-statuskoder. Feil har alltid samme JSON-form: en stabil `error`-kode å forgrene på og, når det er nyttig, `details` med årsaken eller feltene som må rettes.

| Status | error | Betydning |
| --- | --- | --- |
| 400 | `invalid_request` | Forespørselen er ugyldig: `details.fields` lister hvert felt og problemet. |
| 401 | `unauthorized` | Nøkkelen mangler, er ugyldig, tilbakekalt eller utløpt. |
| 403 | `forbidden` | Nøkkelen eller rollen din tillater det ikke (`details.reason`), for eksempel en skriving med skrivebeskyttet nøkkel, eller en sletting. |
| 404 | `not_found` | Ikke funnet, eller ikke synlig for deg. |
| 409 | `dependency_cycle` | Konflikt med planen, for eksempel en avhengighet som ville lukke en syklus (`dependency_cycle`). |
| 429 | `rate_limited` | For mange forespørsler: vent antall sekunder oppgitt i `Retry-After`. |
| 500 | `server_error` | Noe gikk galt hos oss: prøv på nytt med eksponentiell backoff. |

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `error` | string | yes | Stable error code: invalid_request, unauthorized, forbidden, not_found, conflict, dependency_cycle, limit_exceeded, rate_limited, server_error, ... |
| `details` | object |  |  |

```json
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"
      }
    ]
  }
}
```

```json
HTTP 403
{
  "error": "forbidden",
  "details": {
    "reason": "destructive"
  }
}
```

```json
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"
        ]
      }
    ]
  }
}
```

## Hastighetsgrenser

Grensene gjelder per nøkkel, i et glidende vindu på ett minutt: **240** leseforespørsler og **60** skriveforespørsler. Over grensen får du `429 rate_limited` med en `Retry-After`-header, i sekunder.

Vent på `Retry-After` før du prøver på nytt, spre store jobber over tid og samle mange endringer i ett endringssett: opptil 1 000 endringer i én forespørsel og én omberegning.

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

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

## Paginering og søk

Oppgavelister er paginert med en markør. Send `limit` (1–500, standard 100) og, for neste side, `nextCursor` fra forrige svar som `cursor`. Når `nextCursor` er `null`, er det ingen flere sider.

Filtrer med `externalId` (eksakt treff på din egen id), `q` (navnet inneholder), `updatedSince` og `completed`; sorter med `sort=-number` (nyeste først) eller `-updatedAt`. Kortere lister, som prosjekter, medlemmer og avhengigheter, kommer hele i ett svar.

```js
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);
```

## Versjonering

API-et og webhook-nyttelastene er versjonert med dato. Gjeldende versjon er `2026-10-01`: den står i `apiVersion`-feltet i hver hendelse og i `info.version` i OpenAPI-dokumentet.

Innenfor en versjon legger vi bare til: nye endepunkter, nye valgfrie felt, nye hendelsestyper. Felt fjernes eller endrer aldri type uten ny versjon, så ignorer feltene du ikke kjenner.

## API-referanse

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

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `id` | string (uuid) |  |  |
| `email` | string (email) |  |  |
| `name` | string, nullable |  |  |
| `avatarUrl` | string, nullable |  |  |
| `locale` | string |  |  |
| `emailVerified` | boolean |  |  |

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

#### Workspace object

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `id` | string (uuid) |  |  |
| `name` | string |  |  |
| `role` | string |  | One of: `owner`, `admin`, `member`. |

```json
{
  "id": "824650d2-d0b3-4cac-8df8-5743ae9ff65a",
  "name": "Acme Construction",
  "role": "owner"
}
```

#### Current user (connection test)

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

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

Returnerer 200 OK: The user

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `user` | User |  |  |

```json
{
  "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)

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

Returnerer 200 OK: Workspaces

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `workspaces` | array of Workspace |  |  |

```json
{
  "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.

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `id` | string (uuid) |  |  |
| `workspaceId` | string (uuid) |  |  |
| `key` | string |  | Project key, e.g. `CRON` (task keys are `CRON-12`). |
| `keyLocked` | boolean |  |  |
| `name` | string |  |  |
| `description` | string, nullable |  |  |
| `startDate` | string (date) |  |  |
| `calendarId` | string (uuid), nullable |  |  |
| `currency` | string |  |  |
| `scheduleFrom` | string |  | One of: `forward`, `backward`. |
| `deadline` | string (date), nullable |  |  |
| `color` | string, nullable |  |  |
| `settings` | object |  |  |
| `archivedAt` | string (date-time), nullable |  |  |
| `createdBy` | string (uuid), nullable |  |  |
| `createdAt` | string (date-time) |  |  |
| `updatedAt` | string (date-time) |  |  |

```json
{
  "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

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `id` | string |  | `builtin:<slug>` or the id of a template saved by you. |
| `kind` | string |  | One of: `builtin`, `user`. |
| `name` | string |  |  |
| `description` | string, nullable |  |  |
| `taskCount` | integer |  |  |
| `durationDays` | integer |  | Working days from start to finish. |

```json
{
  "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

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `id` | string (uuid) |  |  |
| `name` | string |  |  |
| `index` | integer, nullable |  | Numbered MS Project baseline (1-10); null for the main baseline and the ones you name. |
| `capturedAt` | string (date-time) |  |  |
| `capturedBy` | string (uuid), nullable |  |  |
| `taskCount` | integer |  |  |

```json
{
  "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.

Spørringsparametere:

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `archived` | string |  | One of: `1`. |

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

Returnerer 200 OK: Projects

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `projects` | array of Project |  |  |

```json
{
  "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)

Forespørselsinnhold:

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `name` | string | yes |  |
| `workspaceId` | string (uuid) |  | Defaults to your personal workspace. |
| `key` | string |  | 2-5 letters/digits, starts with a letter. Derived from the name if omitted. |
| `description` | string, nullable |  |  |
| `startDate` | string (date) |  |  |
| `deadline` | string (date), nullable |  |  |
| `currency` | string |  |  |
| `scheduleFrom` | string |  | One of: `forward`, `backward`. |
| `color` | string, nullable |  |  |

```bash
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"}'
```

Returnerer 201 Created: Created

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `project` | Project |  |  |

```json
{
  "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)

Forespørselsinnhold:

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `templateId` | string | yes | Id from `GET /api/templates`. |
| `startDate` | string (date) | yes |  |
| `name` | string |  |  |
| `key` | string |  |  |
| `locale` | string |  | Language 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). |
| `tz` | string |  | IANA time zone of the project calendar. |
| `workspaceId` | string (uuid) |  |  |

```bash
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"
}'
```

Returnerer 201 Created: Created

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `project` | Project |  |  |
| `taskCount` | integer |  |  |
| `conflicts` | array of Conflict |  |  |

```json
{
  "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)

Spørringsparametere:

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `locale` | string |  |  |

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

Returnerer 200 OK: Templates

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `templates` | array of Template |  |  |

```json
{
  "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)

Baneparametere:

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `projectId` | string (uuid) | yes | Project id. |

```bash
curl https://planitude.app/api/projects/40be00e0-7e34-4f7a-864b-5cc986f575b8 \
  -H "Authorization: Bearer $PLANITUDE_API_KEY"
```

Returnerer 200 OK: Project

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `project` | Project |  |  |
| `tasks` | array of Task |  |  |
| `dependencies` | array of Dependency |  |  |

```json
{
  "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)

Baneparametere:

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `projectId` | string (uuid) | yes | Project id. |

Forespørselsinnhold:

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `name` | string |  |  |
| `description` | string, nullable |  |  |
| `startDate` | string (date) |  |  |
| `deadline` | string (date), nullable |  |  |
| `currency` | string |  |  |
| `scheduleFrom` | string |  | One of: `forward`, `backward`. |
| `color` | string, nullable |  |  |
| `archived` | boolean |  |  |

```bash
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"}'
```

Returnerer 200 OK: Updated

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `project` | Project |  |  |
| `conflicts` | array of Conflict |  |  |
| `rescheduled` | boolean |  | The schedule was recalculated (start date, calendar or direction changed). |

```json
{
  "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
}
```

#### List baselines

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

Baneparametere:

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `projectId` | string (uuid) | yes | Project id. |

```bash
curl https://planitude.app/api/projects/40be00e0-7e34-4f7a-864b-5cc986f575b8/baseline \
  -H "Authorization: Bearer $PLANITUDE_API_KEY"
```

Returnerer 200 OK: Baselines

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `baselines` | array of Baseline |  |  |

```json
{
  "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)

Baneparametere:

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `projectId` | string (uuid) | yes | Project id. |

Forespørselsinnhold:

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `name` | string |  |  |

```bash
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"}'
```

Returnerer 201 Created: Saved

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `baseline` | Baseline |  |  |

```json
{
  "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.

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `id` | string (uuid) |  |  |
| `number` | integer |  | Sequential number in the project, never reused. |
| `key` | string |  | Human key, e.g. `CRON-12`. |
| `parentId` | string (uuid), nullable |  |  |
| `name` | string |  |  |
| `type` | string |  | One of: `summary`, `task`, `milestone`. |
| `durationMin` | integer |  | Duration in working minutes (8h day = 480). |
| `workMin` | integer |  |  |
| `progressPct` | number |  |  |
| `constraintKind` | string |  | One 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`. |
| `constraintDate` | string (date-time), nullable |  |  |
| `deadline` | string (date-time), nullable |  |  |
| `calendarId` | string (uuid), nullable |  |  |
| `costFixed` | number |  |  |
| `notes` | string, nullable |  |  |
| `color` | string, nullable |  |  |
| `wbs` | string, nullable |  |  |
| `externalId` | string, nullable |  | Your identifier for this task in another system. Searchable with `GET /tasks?externalId=`. |
| `startAt` | string (date-time), nullable |  | Computed start. |
| `finishAt` | string (date-time), nullable |  | Computed finish. |
| `isCritical` | boolean |  | On the critical path. |
| `totalFloatMin` | integer, nullable |  | Total float (slack) in working minutes. |
| `earlyStartAt` | string (date-time), nullable |  |  |
| `lateFinishAt` | string (date-time), nullable |  |  |
| `actualStartAt` | string (date-time), nullable |  |  |
| `actualFinishAt` | string (date-time), nullable |  |  |
| `actualDurationMin` | integer, nullable |  |  |
| `remainingDurationMin` | integer, nullable |  |  |
| `manuallyScheduled` | boolean |  |  |
| `levelingDelayMin` | integer |  |  |
| `taskType` | string |  | One of: `fixed_units`, `fixed_duration`, `fixed_work`, `fixed_duration_and_units`. |
| `effortDriven` | boolean |  |  |
| `durationOptimisticMin` | integer, nullable |  |  |
| `durationPessimisticMin` | integer, nullable |  |  |
| `projectId` | string (uuid) |  |  |
| `completed` | boolean |  | progressPct = 100. |
| `assignees` | array of Assignee |  |  |
| `url` | string (uri) |  | Page of the task in the web app. |

```json
{
  "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

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `tasks` | array of Task |  | Created/updated tasks plus the ones the recalculation moved. |
| `deletedTaskIds` | array of string |  |  |
| `dependencies` | array of Dependency |  |  |
| `deletedDependencyIds` | array of string |  |  |
| `conflicts` | array of Conflict |  |  |
| `projectStartAt` | string (date-time) |  |  |
| `projectFinishAt` | string (date-time) |  |  |
| `stats` | object |  |  |
| `stats.taskCount` | integer |  |  |
| `stats.recomputedCount` | integer |  | Tasks the scheduling engine recalculated. |
| `stats.savedCount` | integer |  | Tasks whose stored values changed. |
| `stats.durationMs` | integer |  |  |

```json
{
  "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

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `id` | string (uuid) |  |  |
| `taskId` | string (uuid) |  |  |
| `userId` | string (uuid) |  |  |
| `authorName` | string |  |  |
| `authorAvatarUrl` | string, nullable |  |  |
| `body` | string |  |  |
| `mentions` | array of string |  |  |
| `createdAt` | string (date-time) |  |  |
| `editedAt` | string (date-time), nullable |  |  |

```json
{
  "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)

Baneparametere:

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `projectId` | string (uuid) | yes | Project id. |

Spørringsparametere:

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `limit` | integer |  | 1-500. Default: `100`. |
| `cursor` | string |  | `nextCursor` of the previous page. |
| `sort` | string |  | `-number` = newest first (polling triggers). `-updatedAt` returns the first page only. One of: `number`, `-number`, `-updatedAt`. Default: `number`. |
| `externalId` | string |  | Exact match on `externalId`. |
| `q` | string |  | Name contains (case-insensitive). |
| `updatedSince` | string (date-time) |  |  |
| `completed` | string |  | One of: `true`, `false`. |

```bash
curl "https://planitude.app/api/projects/40be00e0-7e34-4f7a-864b-5cc986f575b8/tasks?externalId=crm-4711" \
  -H "Authorization: Bearer $PLANITUDE_API_KEY"
```

Returnerer 200 OK: A page of tasks

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `tasks` | array of Task |  |  |
| `nextCursor` | string, nullable |  |  |

```json
{
  "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.

Baneparametere:

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `projectId` | string (uuid) | yes | Project id. |

Forespørselsinnhold (`TaskCreate`):

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `name` | string | yes |  |
| `parentId` | string (uuid), nullable |  | Parent (summary) task; null = top level. |
| `afterTaskId` | string (uuid), nullable |  | Insert after this sibling. |
| `beforeTaskId` | string (uuid), nullable |  | Insert before this sibling. |
| `type` | string |  | One of: `summary`, `task`, `milestone`. |
| `durationMin` | integer |  | Duration in working minutes (8h day = 480). |
| `workMin` | integer |  |  |
| `progressPct` | number |  |  |
| `constraintKind` | string |  | One 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`. |
| `constraintDate` | string (date-time), nullable |  |  |
| `deadline` | string (date-time), nullable |  |  |
| `calendarId` | string (uuid), nullable |  |  |
| `costFixed` | number |  |  |
| `notes` | string, nullable |  |  |
| `color` | string, nullable |  |  |
| `wbs` | string, nullable |  |  |
| `externalId` | string, nullable |  | Your identifier for this task in another system. Searchable with `GET /tasks?externalId=`. |

Forespørselsinnhold (eller `ChangeSet`):

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `createTasks` | array of TaskCreate |  |  |
| `updateTasks` | array of any |  |  |
| `createDependencies` | array of DependencyCreate |  |  |
| `deleteDependencyIds` | array of string |  |  |
| `fullReschedule` | boolean |  |  |

```bash
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"}'
```

Returnerer 201 Created: Task created

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `tasks` | array of Task |  | Created/updated tasks plus the ones the recalculation moved. |
| `deletedTaskIds` | array of string |  |  |
| `dependencies` | array of Dependency |  |  |
| `deletedDependencyIds` | array of string |  |  |
| `conflicts` | array of Conflict |  |  |
| `projectStartAt` | string (date-time) |  |  |
| `projectFinishAt` | string (date-time) |  |  |
| `stats` | object |  |  |
| `stats.taskCount` | integer |  |  |
| `stats.recomputedCount` | integer |  | Tasks the scheduling engine recalculated. |
| `stats.savedCount` | integer |  | Tasks whose stored values changed. |
| `stats.durationMs` | integer |  |  |

```json
{
  "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)

Baneparametere:

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `projectId` | string (uuid) | yes | Project id. |
| `taskId` | string (uuid) | yes | Task id. |

```bash
curl https://planitude.app/api/projects/40be00e0-7e34-4f7a-864b-5cc986f575b8/tasks/4dbd104a-00cd-4035-8955-255f52ea586a \
  -H "Authorization: Bearer $PLANITUDE_API_KEY"
```

Returnerer 200 OK: The task

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `task` | Task |  |  |

```json
{
  "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.

Baneparametere:

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `projectId` | string (uuid) | yes | Project id. |
| `taskId` | string (uuid) | yes | Task id. |

Forespørselsinnhold:

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `name` | string |  |  |
| `type` | string |  | One of: `summary`, `task`, `milestone`. |
| `durationMin` | integer |  | Duration in working minutes (8h day = 480). |
| `workMin` | integer |  |  |
| `progressPct` | number |  |  |
| `constraintKind` | string |  | One 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`. |
| `constraintDate` | string (date-time), nullable |  |  |
| `deadline` | string (date-time), nullable |  |  |
| `calendarId` | string (uuid), nullable |  |  |
| `costFixed` | number |  |  |
| `notes` | string, nullable |  |  |
| `color` | string, nullable |  |  |
| `wbs` | string, nullable |  |  |
| `externalId` | string, nullable |  | Your identifier for this task in another system. Searchable with `GET /tasks?externalId=`. |
| `placement` | object |  |  |
| `placement.parentId` | string (uuid), nullable |  | Parent (summary) task; null = top level. |
| `placement.afterTaskId` | string (uuid), nullable |  | Insert after this sibling. |
| `placement.beforeTaskId` | string (uuid), nullable |  | Insert before this sibling. |

```bash
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}'
```

Returnerer 200 OK: Updated

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `tasks` | array of Task |  | Created/updated tasks plus the ones the recalculation moved. |
| `deletedTaskIds` | array of string |  |  |
| `dependencies` | array of Dependency |  |  |
| `deletedDependencyIds` | array of string |  |  |
| `conflicts` | array of Conflict |  |  |
| `projectStartAt` | string (date-time) |  |  |
| `projectFinishAt` | string (date-time) |  |  |
| `stats` | object |  |  |
| `stats.taskCount` | integer |  |  |
| `stats.recomputedCount` | integer |  | Tasks the scheduling engine recalculated. |
| `stats.savedCount` | integer |  | Tasks whose stored values changed. |
| `stats.durationMs` | integer |  |  |

```json
{
  "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)

Baneparametere:

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `projectId` | string (uuid) | yes | Project id. |
| `taskId` | string (uuid) | yes | Task id. |

```bash
curl https://planitude.app/api/projects/40be00e0-7e34-4f7a-864b-5cc986f575b8/tasks/4dbd104a-00cd-4035-8955-255f52ea586a/comments \
  -H "Authorization: Bearer $PLANITUDE_API_KEY"
```

Returnerer 200 OK: Comments

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `comments` | array of Comment |  |  |

```json
{
  "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)

Baneparametere:

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `projectId` | string (uuid) | yes | Project id. |
| `taskId` | string (uuid) | yes | Task id. |

Forespørselsinnhold:

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `body` | string | yes | Plain text. |
| `mentions` | array of string |  | User ids to notify. |

```bash
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."}'
```

Returnerer 201 Created: Created

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `comment` | Comment |  |  |

```json
{
  "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

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `id` | string (uuid) |  |  |
| `predecessorId` | string (uuid) |  |  |
| `successorId` | string (uuid) |  |  |
| `type` | string |  | One of: `FS`, `SS`, `FF`, `SF`. |
| `lagMin` | integer |  | Working minutes; negative = lead. |

```json
{
  "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)

Baneparametere:

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `projectId` | string (uuid) | yes | Project id. |

```bash
curl https://planitude.app/api/projects/40be00e0-7e34-4f7a-864b-5cc986f575b8/dependencies \
  -H "Authorization: Bearer $PLANITUDE_API_KEY"
```

Returnerer 200 OK: Dependencies

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `dependencies` | array of Dependency |  |  |

```json
{
  "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`.

Baneparametere:

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `projectId` | string (uuid) | yes | Project id. |

Forespørselsinnhold (`DependencyCreate`):

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `predecessorId` | string (uuid) | yes |  |
| `successorId` | string (uuid) | yes |  |
| `type` | string |  | One of: `FS`, `SS`, `FF`, `SF`. Default: `FS`. |
| `lagMin` | integer |  | Default: `0`. |

Forespørselsinnhold (eller `dependencies`):

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `dependencies` | array of DependencyCreate | yes |  |

```bash
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
}'
```

Returnerer 201 Created: Created

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `tasks` | array of Task |  | Created/updated tasks plus the ones the recalculation moved. |
| `deletedTaskIds` | array of string |  |  |
| `dependencies` | array of Dependency |  |  |
| `deletedDependencyIds` | array of string |  |  |
| `conflicts` | array of Conflict |  |  |
| `projectStartAt` | string (date-time) |  |  |
| `projectFinishAt` | string (date-time) |  |  |
| `stats` | object |  |  |
| `stats.taskCount` | integer |  |  |
| `stats.recomputedCount` | integer |  | Tasks the scheduling engine recalculated. |
| `stats.savedCount` | integer |  | Tasks whose stored values changed. |
| `stats.durationMs` | integer |  |  |

```json
{
  "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)

Baneparametere:

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `projectId` | string (uuid) | yes | Project id. |
| `dependencyId` | string (uuid) | yes |  |

```bash
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"
```

Returnerer 200 OK: Removed

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `tasks` | array of Task |  | Created/updated tasks plus the ones the recalculation moved. |
| `deletedTaskIds` | array of string |  |  |
| `dependencies` | array of Dependency |  |  |
| `deletedDependencyIds` | array of string |  |  |
| `conflicts` | array of Conflict |  |  |
| `projectStartAt` | string (date-time) |  |  |
| `projectFinishAt` | string (date-time) |  |  |
| `stats` | object |  |  |
| `stats.taskCount` | integer |  |  |
| `stats.recomputedCount` | integer |  | Tasks the scheduling engine recalculated. |
| `stats.savedCount` | integer |  | Tasks whose stored values changed. |
| `stats.durationMs` | integer |  |  |

```json
{
  "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

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `userId` | string (uuid) |  |  |
| `name` | string |  |  |
| `email` | string |  |  |
| `role` | string |  | Effective role on the project. One of: `owner`, `editor`, `viewer`. |
| `directRole` | string, nullable |  | Role granted on the project itself (null = only through the workspace). One of: `owner`, `editor`, `viewer`. |
| `inherited` | boolean |  | Access comes from the workspace. |
| `resourceId` | string (uuid), nullable |  | Resource used when the person is assigned to tasks. |
| `addedAt` | string (date-time) |  |  |
| `avatarUrl` | string, nullable |  |  |

```json
{
  "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

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `resourceId` | string (uuid) |  |  |
| `userId` | string (uuid), nullable |  |  |
| `name` | string |  |  |
| `unitsPct` | integer |  |  |

```json
{
  "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.

Baneparametere:

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `projectId` | string (uuid) | yes | Project id. |
| `taskId` | string (uuid) | yes | Task id. |

Forespørselsinnhold:

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `userIds` | array of string | yes |  |
| `resourceIds` | array of string |  |  |

```bash
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"]}'
```

Returnerer 200 OK: Assignees saved

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `taskId` | string (uuid) |  |  |
| `assignments` | array of object |  |  |
| `assignments.id` | string (uuid) |  |  |
| `assignments.taskId` | string (uuid) |  |  |
| `assignments.resourceId` | string (uuid) |  |  |
| `assignments.unitsPct` | integer |  |  |
| `assignments.workMin` | integer |  |  |
| `resources` | array of object |  |  |
| `resources.id` | string (uuid) |  |  |
| `resources.userId` | string (uuid), nullable |  |  |
| `resources.name` | string |  |  |
| `resources.kind` | string |  | One of: `work`, `material`, `cost`. |
| `resources.email` | string, nullable |  |  |
| `resources.costPerHour` | number |  |  |
| `resources.costPerUse` | number |  |  |
| `resources.capacityPct` | integer |  |  |
| `resources.calendarId` | string (uuid), nullable |  |  |
| `resources.color` | string, nullable |  |  |
| `resources.avatarUrl` | string, nullable |  |  |
| `needsReschedule` | boolean |  |  |
| `conflicts` | array of Conflict |  |  |

```json
{
  "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)

Baneparametere:

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `projectId` | string (uuid) | yes | Project id. |

```bash
curl https://planitude.app/api/projects/40be00e0-7e34-4f7a-864b-5cc986f575b8/members \
  -H "Authorization: Bearer $PLANITUDE_API_KEY"
```

Returnerer 200 OK: Members

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `members` | array of Member |  |  |
| `role` | string |  |  |

```json
{
  "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

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `id` | string (uuid) |  |  |
| `workspaceId` | string (uuid) |  |  |
| `workspaceName` | string |  |  |
| `projectId` | string (uuid), nullable |  | null = every project of the workspace. |
| `projectName` | string, nullable |  |  |
| `name` | string |  |  |
| `url` | string (uri) |  |  |
| `events` | array of string |  | 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`. |
| `secretPrefix` | string |  | First characters of the signing secret (not secret). |
| `active` | boolean |  |  |
| `disabledReason` | string, nullable |  | One of: `failures`, `gone`, `project_deleted`, `user`. |
| `disabledAt` | string (date-time), nullable |  |  |
| `consecutiveFailures` | integer |  |  |
| `viaApi` | boolean |  | Created with the API (REST hook) rather than from the profile. |
| `createdBy` | object, nullable |  |  |
| `createdBy.id` | string (uuid) |  |  |
| `createdBy.name` | string, nullable |  |  |
| `createdAt` | string (date-time) |  |  |
| `updatedAt` | string (date-time) |  |  |
| `lastDeliveryAt` | string (date-time), nullable |  |  |
| `lastSuccessAt` | string (date-time), nullable |  |  |

```json
{
  "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

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `id` | string (uuid) |  |  |
| `eventId` | string (uuid) |  |  |
| `eventType` | string |  |  |
| `status` | string |  | One of: `pending`, `success`, `failed`, `cancelled`. |
| `attempts` | integer |  |  |
| `nextAttemptAt` | string (date-time), nullable |  |  |
| `lastAttemptAt` | string (date-time), nullable |  |  |
| `responseStatus` | integer, nullable |  |  |
| `responseExcerpt` | string, nullable |  |  |
| `error` | string, nullable |  |  |
| `durationMs` | integer, nullable |  |  |
| `manual` | boolean |  |  |
| `createdAt` | string (date-time) |  |  |

```json
{
  "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

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `id` | string (uuid) | yes | Event id, also in the `Planitude-Event-Id` header: use it to deduplicate. |
| `type` | string | yes | 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`, `webhook.ping`. |
| `apiVersion` | string | yes | One of: `2026-10-01`. |
| `createdAt` | string (date-time) | yes |  |
| `workspaceId` | string (uuid) | yes |  |
| `projectId` | string (uuid), nullable |  |  |
| `actor` | object, nullable |  | Who made the change (null for system changes). |
| `actor.id` | string (uuid) |  |  |
| `actor.name` | string, nullable |  |  |
| `data` | object | yes |  |

```json
{
  "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.

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `id` | string (uuid) |  |  |
| `workspaceId` | string (uuid) |  |  |
| `key` | string |  |  |
| `name` | string |  |  |
| `description` | string, nullable |  |  |
| `startDate` | string (date) |  |  |
| `deadline` | string (date), nullable |  |  |
| `currency` | string |  |  |
| `scheduleFrom` | string |  | One of: `forward`, `backward`. |
| `color` | string, nullable |  |  |
| `archived` | boolean |  |  |
| `createdAt` | string (date-time) |  |  |
| `updatedAt` | string (date-time) |  |  |
| `url` | string (uri) |  |  |

```json
{
  "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)

Spørringsparametere:

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `workspaceId` | string (uuid) |  |  |
| `projectId` | string (uuid) |  |  |

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

Returnerer 200 OK: Subscriptions

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `webhooks` | array of Webhook |  |  |

```json
{
  "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.

Forespørselsinnhold:

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `url` | string (uri) | yes | HTTPS endpoint that receives the events. Public addresses only. |
| `events` | array of string | yes | `*` = 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`, `*`. |
| `name` | string |  |  |
| `projectId` | string (uuid) |  | Limit to one project (editor role needed). Omit for the whole workspace (owner/admin). |
| `workspaceId` | string (uuid) |  |  |

```bash
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"
}'
```

Returnerer 201 Created: Created

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `webhook` | Webhook |  |  |
| `secret` | string |  | whsec_... |

```json
{
  "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)

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

Returnerer 200 OK: Events

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `apiVersion` | string |  |  |
| `events` | array of string |  |  |

```json
{
  "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)

Baneparametere:

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `webhookId` | string (uuid) | yes | Webhook id. |

```bash
curl https://planitude.app/api/webhooks/1747ee7f-a395-4d3c-94b0-54b513d71d88 \
  -H "Authorization: Bearer $PLANITUDE_API_KEY"
```

Returnerer 200 OK: Subscription

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `webhook` | Webhook |  |  |

```json
{
  "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)

Baneparametere:

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `webhookId` | string (uuid) | yes | Webhook id. |

Forespørselsinnhold:

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `name` | string |  |  |
| `url` | string (uri) |  |  |
| `events` | array of string |  |  |
| `active` | boolean |  | false = pause; true = resume (also resets the failure counter). |

```bash
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}'
```

Returnerer 200 OK: Updated

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `webhook` | Webhook |  |  |

```json
{
  "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)

Baneparametere:

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `webhookId` | string (uuid) | yes | Webhook id. |

```bash
curl -X DELETE https://planitude.app/api/webhooks/1747ee7f-a395-4d3c-94b0-54b513d71d88 \
  -H "Authorization: Bearer $PLANITUDE_API_KEY"
```

Returnerer 204 No Content: Deleted

#### Delivery log (newest first, kept 30 days)

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

Baneparametere:

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `webhookId` | string (uuid) | yes | Webhook id. |

Spørringsparametere:

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `limit` | integer |  | 1-100. Default: `50`. |
| `before` | string (date-time) |  | `nextCursor` of the previous page. |

```bash
curl https://planitude.app/api/webhooks/1747ee7f-a395-4d3c-94b0-54b513d71d88/deliveries \
  -H "Authorization: Bearer $PLANITUDE_API_KEY"
```

Returnerer 200 OK: Deliveries

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `deliveries` | array of Delivery |  |  |
| `nextCursor` | string, nullable |  |  |

```json
{
  "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)

Baneparametere:

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `webhookId` | string (uuid) | yes | Webhook id. |
| `deliveryId` | string (uuid) | yes |  |

```bash
curl https://planitude.app/api/webhooks/1747ee7f-a395-4d3c-94b0-54b513d71d88/deliveries/4b207161-80f9-40bb-bc3f-d8f70315be00 \
  -H "Authorization: Bearer $PLANITUDE_API_KEY"
```

Returnerer 200 OK: Payload

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `payload` | Event |  |  |

```json
{
  "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)

Baneparametere:

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `webhookId` | string (uuid) | yes | Webhook id. |
| `deliveryId` | string (uuid) | yes |  |

```bash
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"
```

Returnerer 202 Accepted: Queued

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `delivery` | Delivery |  |  |

```json
{
  "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)

Baneparametere:

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `webhookId` | string (uuid) | yes | Webhook id. |

```bash
curl -X POST https://planitude.app/api/webhooks/1747ee7f-a395-4d3c-94b0-54b513d71d88/test \
  -H "Authorization: Bearer $PLANITUDE_API_KEY"
```

Returnerer 202 Accepted: Queued

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `delivery` | Delivery |  |  |

```json
{
  "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)

Baneparametere:

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `webhookId` | string (uuid) | yes | Webhook id. |

```bash
curl -X POST https://planitude.app/api/webhooks/1747ee7f-a395-4d3c-94b0-54b513d71d88/rotate-secret \
  -H "Authorization: Bearer $PLANITUDE_API_KEY"
```

Returnerer 200 OK: New secret (shown once)

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `webhook` | Webhook |  |  |
| `secret` | string |  |  |

```json
{
  "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 sender en HTTPS-`POST` med JSON-innhold til endepunktet ditt så snart noe endres, enten endringen kommer fra nettappen, API-et, MCP-serveren eller en import. En webhook dekker ett prosjekt, eller alle prosjektene i et arbeidsområde (eiere og administratorer).

Opprett dem i [Profil → Webhooks](https://planitude.app/no/app/profile/webhooks) eller med API-et, som REST-hooks: abonner med `POST /api/webhooks`, avslutt abonnementet med `DELETE`. Signeringshemmeligheten vises bare én gang.

| Header | Beskrivelse |
| --- | --- |
| `Planitude-Signature` | `t=<unix seconds>,v1=<hex HMAC-SHA256>`: see Verify signatures. |
| `Planitude-Event` | Event type, e.g. `task.completed`. |
| `Planitude-Event-Id` | Event id, the same in every retry: use it to deduplicate. |
| `Planitude-Delivery` | Id of this delivery (changes with a manual resend). |
| `User-Agent` | `Planitude-Webhooks/1.0 (+https://planitude.app/developers)` |
| `Content-Type` | `application/json` |

### Hendelsestyper

Abonner på enkeltstående hendelser, eller på `*` for alle. Alle hendelser har samme konvolutt; `data` endres med typen.

| Hendelse | Beskrivelse |
| --- | --- |
| `project.created` | A project was created (blank, from a template, duplicated or imported from MS Project). |
| `project.updated` | Project fields changed (name, dates, deadline, archive...), or a saved version was restored. |
| `project.deleted` | A project was deleted. |
| `task.created` | A task was created. |
| `task.updated` | Task fields or assignees changed. `changes` lists only the fields that changed. |
| `task.deleted` | A task was deleted (one event per task of a deleted branch). |
| `task.completed` | Task progress reached 100%. |
| `task.dates_changed` | Start or finish moved, because of an edit (`cause: edit`) or because the schedule was recalculated (`cause: reschedule`). |
| `dependency.created` | A dependency was created or changed. |
| `dependency.deleted` | A dependency was removed. |
| `baseline.saved` | A baseline was saved. |
| `member.invited` | An invite link was created for the project. |
| `member.joined` | A person joined the project. |
| `comment.created` | A comment was added to a task. |

#### project.created

```json
{
  "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

```json
{
  "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

```json
{
  "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

```json
{
  "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

```json
{
  "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

```json
{
  "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

```json
{
  "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

```json
{
  "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

```json
{
  "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

```json
{
  "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

```json
{
  "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

```json
{
  "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

```json
{
  "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

```json
{
  "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"
    }
  }
}
```

### Verifiser signaturer

Hver forespørsel er signert med HMAC-SHA256 og webhookens hemmelighet. `Planitude-Signature`-headeren inneholder et tidsstempel `t` og signaturen `v1`, beregnet på `t`, et punktum og rå body.

1. Bruk rå body, før noen JSON-parsing: en re-serialisert body har en annen signatur.
2. Beregn HMAC og sammenlign i konstant tid.
3. Avvis forespørsler med tidsstempel mer enn 5 minutter fra klokken din: en fanget forespørsel kan ikke spilles av på nytt.
4. Roter hemmeligheten når du vil: den gamle slutter å virke umiddelbart.

```js
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
// });
```

```python
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"])
```

### Nye forsøk og levering

Svar med en hvilken som helst **2xx** innen 10 sekunder og gjør selve jobben etterpå, i en kø. Omdirigeringer følges ikke. Ved feil og tidsavbrudd prøver Planitude opptil 6 ganger totalt: umiddelbart, deretter etter 1 min, 5 min, 30 min, 2 h, 12 h.

Leveringen er **minst én gang** og **uten garantert rekkefølge**. Lagre hendelses-id-en (`Planitude-Event-Id`, også `id` i body) og hopp over de du allerede har behandlet; sorter etter `createdAt`.

Når 5 hendelser på rad mislykkes på alle forsøk, deaktiveres webhooken og eieren får en e-post. Et `410 Gone`-svar deaktiverer den umiddelbart. Slå den på igjen fra profilen, eller med `PATCH` og `active: true`.

Leveringsloggen (status, HTTP-kode, utdrag av svaret, nyttelast, manuell gjensending) lagres i 30 dager. Bare offentlige adresser godtas som mål.

### Test endepunktet ditt

1. Send en `webhook.ping`-hendelse med testendepunktet, eller med Test-knappen i profilen.
2. For å motta hendelser på datamaskinen din, eksponer den lokale serveren med en tunnel (for eksempel ngrok eller Cloudflare Tunnel) og abonner på den offentlige HTTPS-adressen.
3. Åpne leveringsloggen for å se status, HTTP-kode og svar, og send en levering på nytt når endepunktet er rettet.

```bash
curl -X POST https://planitude.app/api/webhooks/1747ee7f-a395-4d3c-94b0-54b513d71d88/test \
  -H "Authorization: Bearer $PLANITUDE_API_KEY"
```

## MCP-server

Planitude er en MCP-server (Model Context Protocol): AI-assistenter og kodeeditorer leser prosjekter, analyserer kritisk linje og arbeidsbelastning, og oppretter eller endrer oppgaver, med dine tillatelser. Nettapper kobler til med OAuth (du logger inn og gir tilgang); kommandolinjeverktøy og editorer bruker en API-nøkkel.

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

### ChatGPT (OAuth)

I ChatGPT åpner du Innstillinger › Sikkerhet og pålogging og slår på utviklermodus. Opprett en app med server-URL-en og OAuth-autentisering, logg deretter inn på Planitude og gi tilgang.

Server URL:

```
https://planitude.app/mcp
```

Offisiell dokumentasjon: https://developers.openai.com/api/docs/guides/developer-mode

### Claude (OAuth)

Åpne Tilpass › Koblinger › Legg til egendefinert kobling, lim inn server-URL-en og velg Koble til: Planitude åpnes, du logger inn og gir tilgang. Det fungerer i claude.ai, Claude Desktop og mobilappen.

Server URL:

```
https://planitude.app/mcp
```

Offisiell dokumentasjon: https://claude.com/docs/connectors/custom/add-unlisted

### Gemini (OAuth)

På gemini.google.com åpner du Innstillinger › Tilkoblede apper, velger Legg til en egendefinert app og limer inn server-URL-en; logg deretter inn på Planitude og gi tilgang.

Server URL:

```
https://planitude.app/mcp
```

Offisiell dokumentasjon: https://support.google.com/gemini/answer/17209137

### Claude Code (API-nøkkel)

Kjør kommandoen med nøkkelen din i en miljøvariabel. Legg til `--scope user` for å bruke den i alle prosjekter, og sjekk med `claude mcp list`.

Terminal:

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

Offisiell dokumentasjon: https://code.claude.com/docs/en/mcp

### Codex (API-nøkkel)

Legg nøkkelen i `PLANITUDE_API_KEY` og legg blokken til i `~/.codex/config.toml`: den gjelder for CLI-en, appen og IDE-utvidelsen.

~/.codex/config.toml:

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

Offisiell dokumentasjon: https://learn.chatgpt.com/docs/extend/mcp

### Cursor (API-nøkkel)

Legg blokken til i `~/.cursor/mcp.json` (alle prosjekter) eller `.cursor/mcp.json` (ett prosjekt); Cursor leser nøkkelen fra miljøet.

~/.cursor/mcp.json:

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

Offisiell dokumentasjon: https://cursor.com/docs/context/mcp

### Visual Studio Code (API-nøkkel)

Legg `.vscode/mcp.json` til i arbeidsområdet: VS Code spør om nøkkelen én gang og lagrer den sikkert, utenfor filen.

.vscode/mcp.json:

```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
    }
  ]
}
```

Offisiell dokumentasjon: https://code.visualstudio.com/docs/copilot/customization/mcp-servers

### Gemini CLI (API-nøkkel)

Kjør kommandoen med nøkkelen din i en miljøvariabel, eller legg blokken til i `~/.gemini/settings.json`.

Terminal:

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

~/.gemini/settings.json:

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

Offisiell dokumentasjon: https://geminicli.com/docs/tools/mcp-server/

### Verktøy

Serveren tilbyr 29 verktøy, fra lesing og analyse til trygge endringer. Ingen av dem sletter prosjekter eller oppgaver.

- 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`

[Trinn-for-trinn-veiledning for ChatGPT, Claude og Gemini](https://planitude.app/no/blog/chatgpt-claude-gemini-mcp-project-plans)

## Ferdige integrasjoner

Ingen kode nødvendig: disse integrasjonene bruker samme API og webhooks og settes opp med noen få klikk.

- [Jira](https://planitude.app/no/integrations/jira) (Tilgjengelig): Toveis synkronisering av oppgaver, datoer og avhengigheter med Jira-saker.
- [Azure DevOps](https://planitude.app/no/integrations/azure-devops) (Tilgjengelig): Toveis synkronisering med arbeidselementer i Azure DevOps Boards.
- [Slack](https://planitude.app/no/integrations/slack) (Tilgjengelig): Prosjektoppdateringer, daglige sammendrag og en slash-kommando i kanalene dine.
- [Microsoft Teams](https://planitude.app/no/integrations/teams) (Tilgjengelig): Oppdateringer, sammendrag og en Planitude-fane i Microsoft Teams.
- [Zapier, Make, n8n](https://planitude.app/no/integrations?category=automation) (Kommer snart): Umiddelbare utløsere og handlinger på oppgaver og prosjekter for automatiseringer uten kode.

## Endringslogg

- **2026-10-02**: Ny utviklerdokumentasjon, generert fra OpenAPI-dokumentet. OpenAPI-dokumentet beskriver nå hvert felt som returneres for medlemmer, kommentarer, grunnlinjer, maler og tildelte.
- **2026-10-01**: API-versjon 2026-10-01: signerte utgående webhooks, dokumentert offentlig REST API (OpenAPI 3.1), markørpaginering og søk etter externalId.
- **2026-09-30**: OAuth 2.1 for MCP-serveren og API-et: ChatGPT, Claude og Gemini kobler til uten å kopiere en nøkkel.
- **2026-09-28**: MCP-server og personlige API-nøkler med lese- eller lese- og skrivetilgang.
