개발자
Planitude로 개발하기
REST API, 서명된 웹훅, MCP 서버로 시스템을 계획과 동기화하고, 모든 변경에 대응하며, AI 어시스턴트가 프로젝트에서 작업하도록 하세요.
대부분의 API 문서와 마찬가지로 코드, 필드 이름, 기술 레퍼런스는 영어로 제공됩니다.
- 시스템과 작업 동기화CRM, ERP 또는 스크립트에서 프로젝트와 작업을 만들고,
externalId로 자체 ID를 사용해 다시 찾으며, 날짜는 일정 계산 엔진이 산출하도록 하세요. - 모든 변경에 대응작업이 생성, 완료 또는 이동될 때마다 서명된 HTTPS 요청을 받습니다. 변경이 앱, API, 가져오기, AI 어시스턴트 중 어디에서 발생했든 동일합니다.
- AI 어시스턴트 연결ChatGPT, Claude, Gemini, Cursor, VS Code가 MCP 서버를 통해 계획을 읽고 변경을 제안합니다. 사용자의 권한 범위 내에서만 동작합니다.
기본 URL: https://planitude.app
요청과 응답은 HTTPS 기반 JSON입니다. 기간과 지연 기간은 근무 분 단위(8시간 근무일은 480), 시점은 UTC 기준 ISO 8601, 시간이 없는 날짜는 YYYY-MM-DD 형식입니다.
curl https://planitude.app/api/auth/me \
-H "Authorization: Bearer $PLANITUDE_API_KEY"const res = await fetch("https://planitude.app/api/auth/me", {
headers: {
Authorization: `Bearer ${process.env.PLANITUDE_API_KEY}`,
},
});
if (!res.ok) throw new Error(`${res.status} ${await res.text()}`);
console.log(await res.json());import os
import requests
res = requests.get(
"https://planitude.app/api/auth/me",
headers={"Authorization": f"Bearer {os.environ['PLANITUDE_API_KEY']}"},
)
res.raise_for_status()
print(res.json()){
"user": {
"id": "b1666432-132a-4ea9-b1ae-76ea69e226c5",
"email": "[email protected]",
"name": "Anna Rossi",
"avatarUrl": null,
"locale": "en",
"emailVerified": true
}
}빠른 시작
세 단계로 처음부터 첫 웹훅까지 완료합니다. 터미널만 있으면 됩니다.
API 키 만들기
프로필 → API 키를 열고 읽기 및 쓰기를 선택한 뒤 키를 복사하세요. 키는
gnt_로 시작하며 한 번만 표시됩니다. 환경 변수에 보관하세요.첫 번째 호출 실행
볼 수 있는 프로젝트를 나열합니다. 키는 사용자와 동일하게 동작하므로 프로젝트와 역할이 같습니다.
웹훅 수신
필요한 이벤트에 공개 HTTPS 엔드포인트를 구독시킨 뒤 곧바로 테스트 이벤트를 보내 서명을 검증하세요.
export PLANITUDE_API_KEY="gnt_…"curl https://planitude.app/api/projects \
-H "Authorization: Bearer $PLANITUDE_API_KEY"const res = await fetch("https://planitude.app/api/projects", {
headers: {
Authorization: `Bearer ${process.env.PLANITUDE_API_KEY}`,
},
});
if (!res.ok) throw new Error(`${res.status} ${await res.text()}`);
console.log(await res.json());import os
import requests
res = requests.get(
"https://planitude.app/api/projects",
headers={"Authorization": f"Bearer {os.environ['PLANITUDE_API_KEY']}"},
)
res.raise_for_status()
print(res.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"
}
]
}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"
}'const res = await fetch("https://planitude.app/api/webhooks", {
method: "POST",
headers: {
Authorization: `Bearer ${process.env.PLANITUDE_API_KEY}`,
"Content-Type": "application/json",
},
body: JSON.stringify({
url: "https://example.com/hooks/planitude",
events: ["task.created", "task.completed"],
projectId: "40be00e0-7e34-4f7a-864b-5cc986f575b8",
}),
});
if (!res.ok) throw new Error(`${res.status} ${await res.text()}`);
console.log(await res.json());import os
import requests
res = requests.post(
"https://planitude.app/api/webhooks",
headers={"Authorization": f"Bearer {os.environ['PLANITUDE_API_KEY']}"},
json={
"url": "https://example.com/hooks/planitude",
"events": ["task.created", "task.completed"],
"projectId": "40be00e0-7e34-4f7a-864b-5cc986f575b8",
},
)
res.raise_for_status()
print(res.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"
]
}
}
}인증
모든 요청은 Authorization 헤더에 베어러 토큰으로 개인 API 키를 담아 인증합니다. 키는 개인용이며 각 프로젝트에서 사용자의 역할 범위 내에서만 동작하고 그 이상은 허용되지 않습니다. 키를 하나의 워크스페이스로 제한하거나 만료일을 설정하고 언제든지 해제할 수 있으며, 모든 호출은 키와 함께 기록됩니다.
스코프
| 스코프 | 허용 범위 |
|---|---|
| readplanitude:read | 프로젝트, 작업, 종속 관계, 구성원, 댓글, 기준선, 웹훅을 읽습니다. |
| writeplanitude:write | 읽기 권한의 모든 기능에 더해 프로젝트, 작업, 종속 관계, 댓글, 기준선, 웹훅을 생성하고 변경합니다. |
키와 OAuth 앱은 프로젝트나 작업을 삭제할 수 없습니다. 삭제는 사용자가 직접 확인하는 웹 앱에서만 가능합니다. 잘못된 ID를 대상으로 한 스크립트가 계획을 지울 수 없습니다.
타사 앱용 OAuth 2.1
다른 사용자를 대신해 동작하는 앱은 OAuth 2.1을 사용합니다. PKCE(S256)를 적용한 인증 코드 방식, 사용할 때마다 교체되는 리프레시 토큰, 동적 클라이언트 등록(RFC 7591)을 지원합니다. 리소스 https://planitude.app/api와 스코프 planitude:read 또는 planitude:write를 요청하세요.
엔드포인트는 표준 디스커버리 문서에 게시되므로 OAuth 라이브러리가 자동으로 설정됩니다.
https://planitude.app/.well-known/oauth-authorization-serverhttps://planitude.app/.well-known/oauth-protected-resource
권장 사항
- 키는 환경 변수 또는 시크릿 관리자에 보관하고 코드나 저장소에는 절대 넣지 마세요. 키는
gnt_로 시작하므로 시크릿 스캐너가 탐지할 수 있습니다. - 읽기만 할 때는 읽기 전용 키를 사용하고 연동마다 키를 하나씩 사용하세요. 다른 연동을 중단하지 않고 하나만 해제할 수 있습니다.
- 스크립트에서 사용하는 키에는 만료일을 설정하고 주기적으로 교체하세요.
- API는 브라우저가 아닌 서버에서 호출하세요. 프런트엔드 코드에 있는 키는 공개된 키나 마찬가지입니다.
curl https://planitude.app/api/projects \
-H "Authorization: Bearer $PLANITUDE_API_KEY"{
"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"
]
}오류
Planitude는 표준 HTTP 상태 코드를 사용합니다. 오류는 항상 동일한 JSON 구조를 가집니다. 분기 처리에 쓸 수 있는 안정적인 error 코드와, 필요한 경우 원인이나 수정할 필드를 담은 details가 포함됩니다.
| 상태 | 의미 |
|---|---|
| 400invalid_request | 요청이 올바르지 않습니다. details.fields에 각 필드와 문제가 나열됩니다. |
| 401unauthorized | 키가 없거나, 유효하지 않거나, 해제되었거나, 만료되었습니다. |
| 403forbidden | 키 또는 사용자의 역할로는 허용되지 않습니다(details.reason). 예: 읽기 전용 키로 쓰기를 시도하거나 삭제를 시도한 경우. |
| 404not_found | 찾을 수 없거나 사용자에게 보이지 않습니다. |
| 409dependency_cycle | 계획과 충돌합니다. 예: 순환을 만드는 종속 관계(dependency_cycle). |
| 429rate_limited | 요청이 너무 많습니다. Retry-After에 표시된 초만큼 기다리세요. |
| 500server_error | 서버 측에서 문제가 발생했습니다. 지수 백오프로 재시도하세요. |
Error
| 필드 | 유형 | 설명 |
|---|---|---|
| error 필수 | string | Stable error code: invalid_request, unauthorized, forbidden, not_found, conflict, dependency_cycle, limit_exceeded, rate_limited, server_error, ... |
| details | object |
{
"error": "invalid_request",
"details": {
"fields": [
{
"path": "name",
"message": "Invalid input: expected string, received undefined"
},
{
"path": "durationMin",
"message": "Too small: expected number to be >=0"
}
]
}
}{
"error": "forbidden",
"details": {
"reason": "destructive"
}
}{
"error": "dependency_cycle",
"details": {
"cycles": [
{
"path": "Foundations -> Walls -> Foundations",
"taskIds": [
"4dbd104a-00cd-4035-8955-255f52ea586a",
"a22fd799-c4dc-4869-a466-0dd6e96f71ce"
],
"taskNames": [
"Foundations",
"Walls"
]
}
]
}
}요청 한도
한도는 키별로 1분 슬라이딩 윈도우 기준으로 적용됩니다. 읽기 요청 240건, 쓰기 요청 60건입니다. 한도를 초과하면 초 단위의 Retry-After 헤더와 함께 429 rate_limited가 반환됩니다.
재시도하기 전에 Retry-After만큼 기다리고, 대량 작업은 시간을 분산하며, 여러 변경은 하나의 변경 세트로 묶으세요. 한 번의 요청과 한 번의 재계산으로 최대 1,000건의 변경을 처리할 수 있습니다.
HTTP/1.1 429 Too Many Requests
Retry-After: 12
Content-Type: application/json
{
"error": "rate_limited",
"details": {
"retryAfterSec": 12
}
}페이지 매김 및 검색
작업 목록은 커서 방식으로 페이지가 나뉩니다. limit(1–500, 기본값 100)을 전달하고, 다음 페이지는 이전 응답의 nextCursor를 cursor로 전달하세요. nextCursor가 null이면 더 이상 페이지가 없습니다.
externalId(자체 ID와 정확히 일치), q(이름 포함), updatedSince, completed로 필터링하고, sort=-number(최신순) 또는 -updatedAt으로 정렬합니다. 프로젝트, 구성원, 종속 관계처럼 짧은 목록은 한 번의 응답으로 전체가 반환됩니다.
curl "https://planitude.app/api/projects/40be00e0-7e34-4f7a-864b-5cc986f575b8/tasks?limit=500" \
-H "Authorization: Bearer $PLANITUDE_API_KEY"
# next page: pass the nextCursor of the previous response
curl "https://planitude.app/api/projects/40be00e0-7e34-4f7a-864b-5cc986f575b8/tasks?limit=500&cursor=$NEXT_CURSOR" \
-H "Authorization: Bearer $PLANITUDE_API_KEY"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);import os
import requests
tasks, cursor = [], None
while True:
res = requests.get(
"https://planitude.app/api/projects/40be00e0-7e34-4f7a-864b-5cc986f575b8/tasks",
headers={"Authorization": f"Bearer {os.environ['PLANITUDE_API_KEY']}"},
params={"limit": 500, **({"cursor": cursor} if cursor else {})},
)
res.raise_for_status()
page = res.json()
tasks += page["tasks"]
cursor = page["nextCursor"]
if not cursor:
break
print(len(tasks))버전 관리
API와 웹훅 페이로드는 날짜로 버전을 관리합니다. 현재 버전은 2026-10-01이며, 모든 이벤트의 apiVersion 필드와 OpenAPI 문서의 info.version에 해당합니다.
한 버전 내에서는 추가만 이루어집니다. 새 엔드포인트, 새 선택 필드, 새 이벤트 유형이 추가될 수 있으며, 새 버전 없이 필드가 제거되거나 유형이 바뀌는 일은 없으므로 알 수 없는 필드는 무시하세요.
curl https://planitude.app/api/webhooks/eventsconst res = await fetch("https://planitude.app/api/webhooks/events");
if (!res.ok) throw new Error(`${res.status} ${await res.text()}`);
console.log(await res.json());import requests
res = requests.get("https://planitude.app/api/webhooks/events")
res.raise_for_status()
print(res.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"
]
}API 레퍼런스
Account
Who is calling, and their workspaces.
User
속성
| 필드 | 유형 | 설명 |
|---|---|---|
| id | string (uuid) | |
| string (email) | ||
| name | stringnull 허용 | |
| avatarUrl | stringnull 허용 | |
| locale | string | |
| emailVerified | boolean |
{
"id": "b1666432-132a-4ea9-b1ae-76ea69e226c5",
"email": "[email protected]",
"name": "Anna Rossi",
"avatarUrl": null,
"locale": "en",
"emailVerified": true
}Workspace
속성
| 필드 | 유형 | 설명 |
|---|---|---|
| id | string (uuid) | |
| name | string | |
| role | string | 다음 중 하나: |
{
"id": "824650d2-d0b3-4cac-8df8-5743ae9ff65a",
"name": "Acme Construction",
"role": "owner"
}curl https://planitude.app/api/auth/me \
-H "Authorization: Bearer $PLANITUDE_API_KEY"const res = await fetch("https://planitude.app/api/auth/me", {
headers: {
Authorization: `Bearer ${process.env.PLANITUDE_API_KEY}`,
},
});
if (!res.ok) throw new Error(`${res.status} ${await res.text()}`);
console.log(await res.json());import os
import requests
res = requests.get(
"https://planitude.app/api/auth/me",
headers={"Authorization": f"Bearer {os.environ['PLANITUDE_API_KEY']}"},
)
res.raise_for_status()
print(res.json()){
"user": {
"id": "b1666432-132a-4ea9-b1ae-76ea69e226c5",
"email": "[email protected]",
"name": "Anna Rossi",
"avatarUrl": null,
"locale": "en",
"emailVerified": true
}
}Workspaces of the user, with role
GET/api/workspaces
읽기 권한이 있는 키
반환값
200 OK Workspaces
| 필드 | 유형 | 설명 |
|---|---|---|
| workspaces | array of Workspace |
curl https://planitude.app/api/workspaces \
-H "Authorization: Bearer $PLANITUDE_API_KEY"const res = await fetch("https://planitude.app/api/workspaces", {
headers: {
Authorization: `Bearer ${process.env.PLANITUDE_API_KEY}`,
},
});
if (!res.ok) throw new Error(`${res.status} ${await res.text()}`);
console.log(await res.json());import os
import requests
res = requests.get(
"https://planitude.app/api/workspaces",
headers={"Authorization": f"Bearer {os.environ['PLANITUDE_API_KEY']}"},
)
res.raise_for_status()
print(res.json()){
"workspaces": [
{
"id": "824650d2-d0b3-4cac-8df8-5743ae9ff65a",
"name": "Acme Construction",
"role": "owner"
}
]
}API 레퍼런스
Projects
Projects, templates and baselines.
Project
A project as returned by the write endpoints and project listings.
속성
| 필드 | 유형 | 설명 |
|---|---|---|
| id | string (uuid) | |
| workspaceId | string (uuid) | |
| key | string | Project key, e.g. CRON (task keys are CRON-12). |
| keyLocked | boolean | |
| name | string | |
| description | stringnull 허용 | |
| startDate | string (date) | |
| calendarId | string (uuid)null 허용 | |
| currency | string | |
| scheduleFrom | string | 다음 중 하나: |
| deadline | string (date)null 허용 | |
| color | stringnull 허용 | |
| settings | object | |
| archivedAt | string (date-time)null 허용 | |
| createdBy | string (uuid)null 허용 | |
| createdAt | string (date-time) | |
| updatedAt | string (date-time) |
{
"id": "40be00e0-7e34-4f7a-864b-5cc986f575b8",
"workspaceId": "824650d2-d0b3-4cac-8df8-5743ae9ff65a",
"key": "WHX",
"keyLocked": false,
"name": "Warehouse expansion",
"description": null,
"startDate": "2026-10-05",
"calendarId": null,
"currency": "EUR",
"scheduleFrom": "forward",
"deadline": null,
"color": null,
"settings": {},
"archivedAt": null,
"createdBy": "b1666432-132a-4ea9-b1ae-76ea69e226c5",
"createdAt": "2026-10-01T08:30:12.418Z",
"updatedAt": "2026-10-01T08:30:12.418Z"
}Template
속성
| 필드 | 유형 | 설명 |
|---|---|---|
| id | string | builtin:<slug> or the id of a template saved by you. |
| kind | string | 다음 중 하나: |
| name | string | |
| description | stringnull 허용 | |
| taskCount | integer | |
| durationDays | integer | Working days from start to finish. |
{
"id": "builtin:website-launch",
"kind": "builtin",
"name": "Website launch",
"description": "From discovery to go-live: design, content, development, testing and launch.",
"taskCount": 12,
"durationDays": 43
}Baseline
속성
| 필드 | 유형 | 설명 |
|---|---|---|
| id | string (uuid) | |
| name | string | |
| index | integernull 허용 | Numbered MS Project baseline (1-10); null for the main baseline and the ones you name. |
| capturedAt | string (date-time) | |
| capturedBy | string (uuid)null 허용 | |
| taskCount | integer |
{
"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
읽기 권한이 있는 키
All projects visible to the user (not paginated). ?archived=1 includes archived ones.
쿼리 매개변수
| 필드 | 유형 | 설명 |
|---|---|---|
| archived | string | 다음 중 하나: |
반환값
200 OK Projects
| 필드 | 유형 | 설명 |
|---|---|---|
| projects | array of Project |
curl https://planitude.app/api/projects \
-H "Authorization: Bearer $PLANITUDE_API_KEY"const res = await fetch("https://planitude.app/api/projects", {
headers: {
Authorization: `Bearer ${process.env.PLANITUDE_API_KEY}`,
},
});
if (!res.ok) throw new Error(`${res.status} ${await res.text()}`);
console.log(await res.json());import os
import requests
res = requests.get(
"https://planitude.app/api/projects",
headers={"Authorization": f"Bearer {os.environ['PLANITUDE_API_KEY']}"},
)
res.raise_for_status()
print(res.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
쓰기 권한이 있는 키
요청 본문
| 필드 | 유형 | 설명 |
|---|---|---|
| name 필수 | string | |
| 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 | stringnull 허용 | |
| startDate | string (date) | |
| deadline | string (date)null 허용 | |
| currency | string | |
| scheduleFrom | string | 다음 중 하나: |
| color | stringnull 허용 |
반환값
201 Created Created
| 필드 | 유형 | 설명 |
|---|---|---|
| project | Project |
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"}'const res = await fetch("https://planitude.app/api/projects", {
method: "POST",
headers: {
Authorization: `Bearer ${process.env.PLANITUDE_API_KEY}`,
"Content-Type": "application/json",
},
body: JSON.stringify({
name: "Warehouse expansion",
startDate: "2026-10-05",
currency: "EUR",
}),
});
if (!res.ok) throw new Error(`${res.status} ${await res.text()}`);
console.log(await res.json());import os
import requests
res = requests.post(
"https://planitude.app/api/projects",
headers={"Authorization": f"Bearer {os.environ['PLANITUDE_API_KEY']}"},
json={
"name": "Warehouse expansion",
"startDate": "2026-10-05",
"currency": "EUR",
},
)
res.raise_for_status()
print(res.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
쓰기 권한이 있는 키
요청 본문
| 필드 | 유형 | 설명 |
|---|---|---|
| templateId 필수 | string | Id from GET /api/templates. |
| startDate 필수 | string (date) | |
| 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) |
반환값
201 Created Created
| 필드 | 유형 | 설명 |
|---|---|---|
| project | Project | |
| taskCount | integer | |
| conflicts | array of Conflict |
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"
}'const res = await fetch("https://planitude.app/api/projects/from-template", {
method: "POST",
headers: {
Authorization: `Bearer ${process.env.PLANITUDE_API_KEY}`,
"Content-Type": "application/json",
},
body: JSON.stringify({
templateId: "builtin:website-launch",
startDate: "2026-10-05",
name: "Website relaunch",
locale: "en",
}),
});
if (!res.ok) throw new Error(`${res.status} ${await res.text()}`);
console.log(await res.json());import os
import requests
res = requests.post(
"https://planitude.app/api/projects/from-template",
headers={"Authorization": f"Bearer {os.environ['PLANITUDE_API_KEY']}"},
json={
"templateId": "builtin:website-launch",
"startDate": "2026-10-05",
"name": "Website relaunch",
"locale": "en",
},
)
res.raise_for_status()
print(res.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
읽기 권한이 있는 키
쿼리 매개변수
| 필드 | 유형 | 설명 |
|---|---|---|
| locale | string |
반환값
200 OK Templates
| 필드 | 유형 | 설명 |
|---|---|---|
| templates | array of Template |
curl https://planitude.app/api/templates \
-H "Authorization: Bearer $PLANITUDE_API_KEY"const res = await fetch("https://planitude.app/api/templates", {
headers: {
Authorization: `Bearer ${process.env.PLANITUDE_API_KEY}`,
},
});
if (!res.ok) throw new Error(`${res.status} ${await res.text()}`);
console.log(await res.json());import os
import requests
res = requests.get(
"https://planitude.app/api/templates",
headers={"Authorization": f"Bearer {os.environ['PLANITUDE_API_KEY']}"},
)
res.raise_for_status()
print(res.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}
읽기 권한이 있는 키
경로 매개변수
| 필드 | 유형 | 설명 |
|---|---|---|
| projectId 필수 | string (uuid) | Project id. |
반환값
200 OK Project
| 필드 | 유형 | 설명 |
|---|---|---|
| project | Project | |
| tasks | array of Task | |
| dependencies | array of Dependency |
curl https://planitude.app/api/projects/40be00e0-7e34-4f7a-864b-5cc986f575b8 \
-H "Authorization: Bearer $PLANITUDE_API_KEY"const res = await fetch("https://planitude.app/api/projects/40be00e0-7e34-4f7a-864b-5cc986f575b8", {
headers: {
Authorization: `Bearer ${process.env.PLANITUDE_API_KEY}`,
},
});
if (!res.ok) throw new Error(`${res.status} ${await res.text()}`);
console.log(await res.json());import os
import requests
res = requests.get(
"https://planitude.app/api/projects/40be00e0-7e34-4f7a-864b-5cc986f575b8",
headers={"Authorization": f"Bearer {os.environ['PLANITUDE_API_KEY']}"},
)
res.raise_for_status()
print(res.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}
쓰기 권한이 있는 키
경로 매개변수
| 필드 | 유형 | 설명 |
|---|---|---|
| projectId 필수 | string (uuid) | Project id. |
요청 본문
| 필드 | 유형 | 설명 |
|---|---|---|
| name | string | |
| description | stringnull 허용 | |
| startDate | string (date) | |
| deadline | string (date)null 허용 | |
| currency | string | |
| scheduleFrom | string | 다음 중 하나: |
| color | stringnull 허용 | |
| archived | boolean |
반환값
200 OK Updated
| 필드 | 유형 | 설명 |
|---|---|---|
| project | Project | |
| conflicts | array of Conflict | |
| rescheduled | boolean | The schedule was recalculated (start date, calendar or direction changed). |
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"}'const res = await fetch("https://planitude.app/api/projects/40be00e0-7e34-4f7a-864b-5cc986f575b8", {
method: "PATCH",
headers: {
Authorization: `Bearer ${process.env.PLANITUDE_API_KEY}`,
"Content-Type": "application/json",
},
body: JSON.stringify({
deadline: "2026-12-18",
}),
});
if (!res.ok) throw new Error(`${res.status} ${await res.text()}`);
console.log(await res.json());import os
import requests
res = requests.patch(
"https://planitude.app/api/projects/40be00e0-7e34-4f7a-864b-5cc986f575b8",
headers={"Authorization": f"Bearer {os.environ['PLANITUDE_API_KEY']}"},
json={
"deadline": "2026-12-18",
},
)
res.raise_for_status()
print(res.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
읽기 권한이 있는 키
경로 매개변수
| 필드 | 유형 | 설명 |
|---|---|---|
| projectId 필수 | string (uuid) | Project id. |
반환값
200 OK Baselines
| 필드 | 유형 | 설명 |
|---|---|---|
| baselines | array of Baseline |
curl https://planitude.app/api/projects/40be00e0-7e34-4f7a-864b-5cc986f575b8/baseline \
-H "Authorization: Bearer $PLANITUDE_API_KEY"const res = await fetch("https://planitude.app/api/projects/40be00e0-7e34-4f7a-864b-5cc986f575b8/baseline", {
headers: {
Authorization: `Bearer ${process.env.PLANITUDE_API_KEY}`,
},
});
if (!res.ok) throw new Error(`${res.status} ${await res.text()}`);
console.log(await res.json());import os
import requests
res = requests.get(
"https://planitude.app/api/projects/40be00e0-7e34-4f7a-864b-5cc986f575b8/baseline",
headers={"Authorization": f"Bearer {os.environ['PLANITUDE_API_KEY']}"},
)
res.raise_for_status()
print(res.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
쓰기 권한이 있는 키
경로 매개변수
| 필드 | 유형 | 설명 |
|---|---|---|
| projectId 필수 | string (uuid) | Project id. |
요청 본문
| 필드 | 유형 | 설명 |
|---|---|---|
| name | string |
반환값
201 Created Saved
| 필드 | 유형 | 설명 |
|---|---|---|
| baseline | Baseline |
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"}'const res = await fetch("https://planitude.app/api/projects/40be00e0-7e34-4f7a-864b-5cc986f575b8/baseline", {
method: "POST",
headers: {
Authorization: `Bearer ${process.env.PLANITUDE_API_KEY}`,
"Content-Type": "application/json",
},
body: JSON.stringify({
name: "Plan v1",
}),
});
if (!res.ok) throw new Error(`${res.status} ${await res.text()}`);
console.log(await res.json());import os
import requests
res = requests.post(
"https://planitude.app/api/projects/40be00e0-7e34-4f7a-864b-5cc986f575b8/baseline",
headers={"Authorization": f"Bearer {os.environ['PLANITUDE_API_KEY']}"},
json={
"name": "Plan v1",
},
)
res.raise_for_status()
print(res.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
}
}API 레퍼런스
Tasks
Tasks, search by external id, comments.
Task
Task representation of GET /tasks and webhook payloads: the stored task (without sortKey) plus project, assignees and link.
속성
| 필드 | 유형 | 설명 |
|---|---|---|
| id | string (uuid) | |
| number | integer | Sequential number in the project, never reused. |
| key | string | Human key, e.g. CRON-12. |
| parentId | string (uuid)null 허용 | |
| name | string | |
| type | string | 다음 중 하나: |
| durationMin | integer | Duration in working minutes (8h day = 480). |
| workMin | integer | |
| progressPct | number | |
| constraintKind | string | 다음 중 하나: |
| constraintDate | string (date-time)null 허용 | |
| deadline | string (date-time)null 허용 | |
| calendarId | string (uuid)null 허용 | |
| costFixed | number | |
| notes | stringnull 허용 | |
| color | stringnull 허용 | |
| wbs | stringnull 허용 | |
| externalId | stringnull 허용 | Your identifier for this task in another system. Searchable with GET /tasks?externalId=. |
| startAt | string (date-time)null 허용 | Computed start. |
| finishAt | string (date-time)null 허용 | Computed finish. |
| isCritical | boolean | On the critical path. |
| totalFloatMin | integernull 허용 | Total float (slack) in working minutes. |
| earlyStartAt | string (date-time)null 허용 | |
| lateFinishAt | string (date-time)null 허용 | |
| actualStartAt | string (date-time)null 허용 | |
| actualFinishAt | string (date-time)null 허용 | |
| actualDurationMin | integernull 허용 | |
| remainingDurationMin | integernull 허용 | |
| manuallyScheduled | boolean | |
| levelingDelayMin | integer | |
| taskType | string | 다음 중 하나: |
| effortDriven | boolean | |
| durationOptimisticMin | integernull 허용 | |
| durationPessimisticMin | integernull 허용 | |
| projectId | string (uuid) | |
| completed | boolean | progressPct = 100. |
| assignees | array of Assignee | |
| url | string (uri) | Page of the task in the web app. |
{
"id": "4dbd104a-00cd-4035-8955-255f52ea586a",
"number": 1,
"key": "WHX-1",
"parentId": null,
"name": "Foundations",
"type": "task",
"durationMin": 1440,
"workMin": 0,
"progressPct": 0,
"constraintKind": "asap",
"constraintDate": null,
"deadline": null,
"calendarId": null,
"costFixed": 0,
"notes": null,
"color": null,
"wbs": null,
"externalId": "crm-4711",
"startAt": "2026-10-05T06:00:00.000Z",
"finishAt": "2026-10-07T15:00:00.000Z",
"isCritical": true,
"totalFloatMin": 0,
"earlyStartAt": "2026-10-05T06:00:00.000Z",
"lateFinishAt": "2026-10-07T15:00:00.000Z",
"actualStartAt": null,
"actualFinishAt": null,
"actualDurationMin": null,
"remainingDurationMin": null,
"manuallyScheduled": false,
"levelingDelayMin": 0,
"taskType": "fixed_units",
"effortDriven": false,
"durationOptimisticMin": null,
"durationPessimisticMin": null,
"projectId": "40be00e0-7e34-4f7a-864b-5cc986f575b8",
"completed": false,
"assignees": [
{
"resourceId": "0c6a8f52-3d7e-4b19-9a4e-6f1d2c8b7e30",
"userId": "b1666432-132a-4ea9-b1ae-76ea69e226c5",
"name": "Anna Rossi",
"unitsPct": 100
}
],
"url": "https://planitude.app/app/t/WHX-1"
}WriteResult
속성
| 필드 | 유형 | 설명 | |||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 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의 필드 표시
|
{
"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
속성
| 필드 | 유형 | 설명 |
|---|---|---|
| id | string (uuid) | |
| taskId | string (uuid) | |
| userId | string (uuid) | |
| authorName | string | |
| authorAvatarUrl | stringnull 허용 | |
| body | string | |
| mentions | array of string | |
| createdAt | string (date-time) | |
| editedAt | string (date-time)null 허용 |
{
"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
읽기 권한이 있는 키
경로 매개변수
| 필드 | 유형 | 설명 |
|---|---|---|
| projectId 필수 | string (uuid) | Project id. |
쿼리 매개변수
| 필드 | 유형 | 설명 |
|---|---|---|
| limit | integer | 1-500. 기본값: |
| cursor | string | nextCursor of the previous page. |
| sort | string | -number = newest first (polling triggers). -updatedAt returns the first page only.다음 중 하나: 기본값: |
| externalId | string | Exact match on externalId. |
| q | string | Name contains (case-insensitive). |
| updatedSince | string (date-time) | |
| completed | string | 다음 중 하나: |
반환값
200 OK A page of tasks
| 필드 | 유형 | 설명 |
|---|---|---|
| tasks | array of Task | |
| nextCursor | stringnull 허용 |
curl "https://planitude.app/api/projects/40be00e0-7e34-4f7a-864b-5cc986f575b8/tasks?externalId=crm-4711" \
-H "Authorization: Bearer $PLANITUDE_API_KEY"const res = await fetch("https://planitude.app/api/projects/40be00e0-7e34-4f7a-864b-5cc986f575b8/tasks?externalId=crm-4711", {
headers: {
Authorization: `Bearer ${process.env.PLANITUDE_API_KEY}`,
},
});
if (!res.ok) throw new Error(`${res.status} ${await res.text()}`);
console.log(await res.json());import os
import requests
res = requests.get(
"https://planitude.app/api/projects/40be00e0-7e34-4f7a-864b-5cc986f575b8/tasks",
headers={"Authorization": f"Bearer {os.environ['PLANITUDE_API_KEY']}"},
params={
"externalId": "crm-4711",
},
)
res.raise_for_status()
print(res.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
쓰기 권한이 있는 키
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.
경로 매개변수
| 필드 | 유형 | 설명 |
|---|---|---|
| projectId 필수 | string (uuid) | Project id. |
요청 본문
TaskCreate
| 필드 | 유형 | 설명 |
|---|---|---|
| name 필수 | string | |
| parentId | string (uuid)null 허용 | Parent (summary) task; null = top level. |
| afterTaskId | string (uuid)null 허용 | Insert after this sibling. |
| beforeTaskId | string (uuid)null 허용 | Insert before this sibling. |
| type | string | 다음 중 하나: |
| durationMin | integer | Duration in working minutes (8h day = 480). |
| workMin | integer | |
| progressPct | number | |
| constraintKind | string | 다음 중 하나: |
| constraintDate | string (date-time)null 허용 | |
| deadline | string (date-time)null 허용 | |
| calendarId | string (uuid)null 허용 | |
| costFixed | number | |
| notes | stringnull 허용 | |
| color | stringnull 허용 | |
| wbs | stringnull 허용 | |
| externalId | stringnull 허용 | Your identifier for this task in another system. Searchable with GET /tasks?externalId=. |
또는 ChangeSet
| 필드 | 유형 | 설명 |
|---|---|---|
| createTasks | array of TaskCreate | |
| updateTasks | array of any | |
| createDependencies | array of DependencyCreate | |
| deleteDependencyIds | array of string | |
| fullReschedule | boolean |
반환값
201 Created Task created
| 필드 | 유형 | 설명 | |||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 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의 필드 표시
|
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"}'const res = await fetch("https://planitude.app/api/projects/40be00e0-7e34-4f7a-864b-5cc986f575b8/tasks", {
method: "POST",
headers: {
Authorization: `Bearer ${process.env.PLANITUDE_API_KEY}`,
"Content-Type": "application/json",
},
body: JSON.stringify({
name: "Foundations",
durationMin: 1440,
externalId: "crm-4711",
}),
});
if (!res.ok) throw new Error(`${res.status} ${await res.text()}`);
console.log(await res.json());import os
import requests
res = requests.post(
"https://planitude.app/api/projects/40be00e0-7e34-4f7a-864b-5cc986f575b8/tasks",
headers={"Authorization": f"Bearer {os.environ['PLANITUDE_API_KEY']}"},
json={
"name": "Foundations",
"durationMin": 1440,
"externalId": "crm-4711",
},
)
res.raise_for_status()
print(res.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}
읽기 권한이 있는 키
경로 매개변수
| 필드 | 유형 | 설명 |
|---|---|---|
| projectId 필수 | string (uuid) | Project id. |
| taskId 필수 | string (uuid) | Task id. |
반환값
200 OK The task
| 필드 | 유형 | 설명 |
|---|---|---|
| task | Task |
curl https://planitude.app/api/projects/40be00e0-7e34-4f7a-864b-5cc986f575b8/tasks/4dbd104a-00cd-4035-8955-255f52ea586a \
-H "Authorization: Bearer $PLANITUDE_API_KEY"const res = await fetch("https://planitude.app/api/projects/40be00e0-7e34-4f7a-864b-5cc986f575b8/tasks/4dbd104a-00cd-4035-8955-255f52ea586a", {
headers: {
Authorization: `Bearer ${process.env.PLANITUDE_API_KEY}`,
},
});
if (!res.ok) throw new Error(`${res.status} ${await res.text()}`);
console.log(await res.json());import os
import requests
res = requests.get(
"https://planitude.app/api/projects/40be00e0-7e34-4f7a-864b-5cc986f575b8/tasks/4dbd104a-00cd-4035-8955-255f52ea586a",
headers={"Authorization": f"Bearer {os.environ['PLANITUDE_API_KEY']}"},
)
res.raise_for_status()
print(res.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}
쓰기 권한이 있는 키
Only the fields you send change; null clears a field.
경로 매개변수
| 필드 | 유형 | 설명 |
|---|---|---|
| projectId 필수 | string (uuid) | Project id. |
| taskId 필수 | string (uuid) | Task id. |
요청 본문
| 필드 | 유형 | 설명 | ||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| name | string | |||||||||||||
| type | string | 다음 중 하나: | ||||||||||||
| durationMin | integer | Duration in working minutes (8h day = 480). | ||||||||||||
| workMin | integer | |||||||||||||
| progressPct | number | |||||||||||||
| constraintKind | string | 다음 중 하나: | ||||||||||||
| constraintDate | string (date-time)null 허용 | |||||||||||||
| deadline | string (date-time)null 허용 | |||||||||||||
| calendarId | string (uuid)null 허용 | |||||||||||||
| costFixed | number | |||||||||||||
| notes | stringnull 허용 | |||||||||||||
| color | stringnull 허용 | |||||||||||||
| wbs | stringnull 허용 | |||||||||||||
| externalId | stringnull 허용 | Your identifier for this task in another system. Searchable with GET /tasks?externalId=. | ||||||||||||
| placement | object | placement의 필드 표시
|
반환값
200 OK Updated
| 필드 | 유형 | 설명 | |||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 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의 필드 표시
|
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}'const res = await fetch("https://planitude.app/api/projects/40be00e0-7e34-4f7a-864b-5cc986f575b8/tasks/4dbd104a-00cd-4035-8955-255f52ea586a", {
method: "PATCH",
headers: {
Authorization: `Bearer ${process.env.PLANITUDE_API_KEY}`,
"Content-Type": "application/json",
},
body: JSON.stringify({
progressPct: 100,
}),
});
if (!res.ok) throw new Error(`${res.status} ${await res.text()}`);
console.log(await res.json());import os
import requests
res = requests.patch(
"https://planitude.app/api/projects/40be00e0-7e34-4f7a-864b-5cc986f575b8/tasks/4dbd104a-00cd-4035-8955-255f52ea586a",
headers={"Authorization": f"Bearer {os.environ['PLANITUDE_API_KEY']}"},
json={
"progressPct": 100,
},
)
res.raise_for_status()
print(res.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
읽기 권한이 있는 키
경로 매개변수
| 필드 | 유형 | 설명 |
|---|---|---|
| projectId 필수 | string (uuid) | Project id. |
| taskId 필수 | string (uuid) | Task id. |
반환값
200 OK Comments
| 필드 | 유형 | 설명 |
|---|---|---|
| comments | array of Comment |
curl https://planitude.app/api/projects/40be00e0-7e34-4f7a-864b-5cc986f575b8/tasks/4dbd104a-00cd-4035-8955-255f52ea586a/comments \
-H "Authorization: Bearer $PLANITUDE_API_KEY"const res = await fetch("https://planitude.app/api/projects/40be00e0-7e34-4f7a-864b-5cc986f575b8/tasks/4dbd104a-00cd-4035-8955-255f52ea586a/comments", {
headers: {
Authorization: `Bearer ${process.env.PLANITUDE_API_KEY}`,
},
});
if (!res.ok) throw new Error(`${res.status} ${await res.text()}`);
console.log(await res.json());import os
import requests
res = requests.get(
"https://planitude.app/api/projects/40be00e0-7e34-4f7a-864b-5cc986f575b8/tasks/4dbd104a-00cd-4035-8955-255f52ea586a/comments",
headers={"Authorization": f"Bearer {os.environ['PLANITUDE_API_KEY']}"},
)
res.raise_for_status()
print(res.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
쓰기 권한이 있는 키
경로 매개변수
| 필드 | 유형 | 설명 |
|---|---|---|
| projectId 필수 | string (uuid) | Project id. |
| taskId 필수 | string (uuid) | Task id. |
요청 본문
| 필드 | 유형 | 설명 |
|---|---|---|
| body 필수 | string | Plain text. |
| mentions | array of string | User ids to notify. |
반환값
201 Created Created
| 필드 | 유형 | 설명 |
|---|---|---|
| comment | Comment |
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."}'const res = await fetch("https://planitude.app/api/projects/40be00e0-7e34-4f7a-864b-5cc986f575b8/tasks/4dbd104a-00cd-4035-8955-255f52ea586a/comments", {
method: "POST",
headers: {
Authorization: `Bearer ${process.env.PLANITUDE_API_KEY}`,
"Content-Type": "application/json",
},
body: JSON.stringify({
body: "Concrete delivered, we can start the walls.",
}),
});
if (!res.ok) throw new Error(`${res.status} ${await res.text()}`);
console.log(await res.json());import os
import requests
res = requests.post(
"https://planitude.app/api/projects/40be00e0-7e34-4f7a-864b-5cc986f575b8/tasks/4dbd104a-00cd-4035-8955-255f52ea586a/comments",
headers={"Authorization": f"Bearer {os.environ['PLANITUDE_API_KEY']}"},
json={
"body": "Concrete delivered, we can start the walls.",
},
)
res.raise_for_status()
print(res.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
}
}API 레퍼런스
Dependencies
Links between tasks (FS, SS, FF, SF, with lag).
Dependency
속성
| 필드 | 유형 | 설명 |
|---|---|---|
| id | string (uuid) | |
| predecessorId | string (uuid) | |
| successorId | string (uuid) | |
| type | string | 다음 중 하나: |
| lagMin | integer | Working minutes; negative = lead. |
{
"id": "9ba5faf8-923d-4ecc-86f0-f245c5d179db",
"predecessorId": "4dbd104a-00cd-4035-8955-255f52ea586a",
"successorId": "a22fd799-c4dc-4869-a466-0dd6e96f71ce",
"type": "FS",
"lagMin": 0
}List dependencies
GET/api/projects/{projectId}/dependencies
읽기 권한이 있는 키
경로 매개변수
| 필드 | 유형 | 설명 |
|---|---|---|
| projectId 필수 | string (uuid) | Project id. |
반환값
200 OK Dependencies
| 필드 | 유형 | 설명 |
|---|---|---|
| dependencies | array of Dependency |
curl https://planitude.app/api/projects/40be00e0-7e34-4f7a-864b-5cc986f575b8/dependencies \
-H "Authorization: Bearer $PLANITUDE_API_KEY"const res = await fetch("https://planitude.app/api/projects/40be00e0-7e34-4f7a-864b-5cc986f575b8/dependencies", {
headers: {
Authorization: `Bearer ${process.env.PLANITUDE_API_KEY}`,
},
});
if (!res.ok) throw new Error(`${res.status} ${await res.text()}`);
console.log(await res.json());import os
import requests
res = requests.get(
"https://planitude.app/api/projects/40be00e0-7e34-4f7a-864b-5cc986f575b8/dependencies",
headers={"Authorization": f"Bearer {os.environ['PLANITUDE_API_KEY']}"},
)
res.raise_for_status()
print(res.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
쓰기 권한이 있는 키
A dependency that would close a cycle is refused with 409 dependency_cycle.
경로 매개변수
| 필드 | 유형 | 설명 |
|---|---|---|
| projectId 필수 | string (uuid) | Project id. |
요청 본문
DependencyCreate
| 필드 | 유형 | 설명 |
|---|---|---|
| predecessorId 필수 | string (uuid) | |
| successorId 필수 | string (uuid) | |
| type | string | 다음 중 하나: 기본값: |
| lagMin | integer | 기본값: |
또는 dependencies
| 필드 | 유형 | 설명 |
|---|---|---|
| dependencies 필수 | array of DependencyCreate |
반환값
201 Created Created
| 필드 | 유형 | 설명 | |||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 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의 필드 표시
|
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
}'const res = await fetch("https://planitude.app/api/projects/40be00e0-7e34-4f7a-864b-5cc986f575b8/dependencies", {
method: "POST",
headers: {
Authorization: `Bearer ${process.env.PLANITUDE_API_KEY}`,
"Content-Type": "application/json",
},
body: JSON.stringify({
predecessorId: "4dbd104a-00cd-4035-8955-255f52ea586a",
successorId: "a22fd799-c4dc-4869-a466-0dd6e96f71ce",
type: "FS",
lagMin: 0,
}),
});
if (!res.ok) throw new Error(`${res.status} ${await res.text()}`);
console.log(await res.json());import os
import requests
res = requests.post(
"https://planitude.app/api/projects/40be00e0-7e34-4f7a-864b-5cc986f575b8/dependencies",
headers={"Authorization": f"Bearer {os.environ['PLANITUDE_API_KEY']}"},
json={
"predecessorId": "4dbd104a-00cd-4035-8955-255f52ea586a",
"successorId": "a22fd799-c4dc-4869-a466-0dd6e96f71ce",
"type": "FS",
"lagMin": 0,
},
)
res.raise_for_status()
print(res.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
DEL/api/projects/{projectId}/dependencies/{dependencyId}
쓰기 권한이 있는 키
경로 매개변수
| 필드 | 유형 | 설명 |
|---|---|---|
| projectId 필수 | string (uuid) | Project id. |
| dependencyId 필수 | string (uuid) |
반환값
200 OK Removed
| 필드 | 유형 | 설명 | |||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 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의 필드 표시
|
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"const res = await fetch("https://planitude.app/api/projects/40be00e0-7e34-4f7a-864b-5cc986f575b8/dependencies/9ba5faf8-923d-4ecc-86f0-f245c5d179db", {
method: "DELETE",
headers: {
Authorization: `Bearer ${process.env.PLANITUDE_API_KEY}`,
},
});
if (!res.ok) throw new Error(`${res.status} ${await res.text()}`);
console.log(res.status); // 204import os
import requests
res = requests.delete(
"https://planitude.app/api/projects/40be00e0-7e34-4f7a-864b-5cc986f575b8/dependencies/9ba5faf8-923d-4ecc-86f0-f245c5d179db",
headers={"Authorization": f"Bearer {os.environ['PLANITUDE_API_KEY']}"},
)
res.raise_for_status()
print(res.status_code) # 204{
"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
}
}API 레퍼런스
People
Project members and task assignees.
Member
속성
| 필드 | 유형 | 설명 |
|---|---|---|
| userId | string (uuid) | |
| name | string | |
| string | ||
| role | string | Effective role on the project. 다음 중 하나: |
| directRole | stringnull 허용 | Role granted on the project itself (null = only through the workspace). 다음 중 하나: |
| inherited | boolean | Access comes from the workspace. |
| resourceId | string (uuid)null 허용 | Resource used when the person is assigned to tasks. |
| addedAt | string (date-time) | |
| avatarUrl | stringnull 허용 |
{
"userId": "b1666432-132a-4ea9-b1ae-76ea69e226c5",
"name": "Anna Rossi",
"email": "[email protected]",
"role": "owner",
"directRole": null,
"inherited": false,
"resourceId": "0c6a8f52-3d7e-4b19-9a4e-6f1d2c8b7e30",
"addedAt": "2026-10-01T08:30:12.418Z",
"avatarUrl": null
}Assignee
속성
| 필드 | 유형 | 설명 |
|---|---|---|
| resourceId | string (uuid) | |
| userId | string (uuid)null 허용 | |
| name | string | |
| unitsPct | integer |
{
"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
쓰기 권한이 있는 키
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.
경로 매개변수
| 필드 | 유형 | 설명 |
|---|---|---|
| projectId 필수 | string (uuid) | Project id. |
| taskId 필수 | string (uuid) | Task id. |
요청 본문
| 필드 | 유형 | 설명 |
|---|---|---|
| userIds 필수 | array of string | |
| resourceIds | array of string |
반환값
200 OK Assignees saved
| 필드 | 유형 | 설명 | ||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| taskId | string (uuid) | |||||||||||||||||||||||||||||||||||||
| assignments | array of object | assignments의 필드 표시
| ||||||||||||||||||||||||||||||||||||
| resources | array of object | resources의 필드 표시
| ||||||||||||||||||||||||||||||||||||
| needsReschedule | boolean | |||||||||||||||||||||||||||||||||||||
| conflicts | array of Conflict |
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"]}'const res = await fetch("https://planitude.app/api/projects/40be00e0-7e34-4f7a-864b-5cc986f575b8/tasks/4dbd104a-00cd-4035-8955-255f52ea586a/assignees", {
method: "PUT",
headers: {
Authorization: `Bearer ${process.env.PLANITUDE_API_KEY}`,
"Content-Type": "application/json",
},
body: JSON.stringify({
userIds: ["b1666432-132a-4ea9-b1ae-76ea69e226c5"],
}),
});
if (!res.ok) throw new Error(`${res.status} ${await res.text()}`);
console.log(await res.json());import os
import requests
res = requests.put(
"https://planitude.app/api/projects/40be00e0-7e34-4f7a-864b-5cc986f575b8/tasks/4dbd104a-00cd-4035-8955-255f52ea586a/assignees",
headers={"Authorization": f"Bearer {os.environ['PLANITUDE_API_KEY']}"},
json={
"userIds": ["b1666432-132a-4ea9-b1ae-76ea69e226c5"],
},
)
res.raise_for_status()
print(res.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": "[email protected]",
"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
읽기 권한이 있는 키
경로 매개변수
| 필드 | 유형 | 설명 |
|---|---|---|
| projectId 필수 | string (uuid) | Project id. |
반환값
200 OK Members
| 필드 | 유형 | 설명 |
|---|---|---|
| members | array of Member | |
| role | string |
curl https://planitude.app/api/projects/40be00e0-7e34-4f7a-864b-5cc986f575b8/members \
-H "Authorization: Bearer $PLANITUDE_API_KEY"const res = await fetch("https://planitude.app/api/projects/40be00e0-7e34-4f7a-864b-5cc986f575b8/members", {
headers: {
Authorization: `Bearer ${process.env.PLANITUDE_API_KEY}`,
},
});
if (!res.ok) throw new Error(`${res.status} ${await res.text()}`);
console.log(await res.json());import os
import requests
res = requests.get(
"https://planitude.app/api/projects/40be00e0-7e34-4f7a-864b-5cc986f575b8/members",
headers={"Authorization": f"Bearer {os.environ['PLANITUDE_API_KEY']}"},
)
res.raise_for_status()
print(res.json()){
"members": [
{
"userId": "b1666432-132a-4ea9-b1ae-76ea69e226c5",
"name": "Anna Rossi",
"email": "[email protected]",
"role": "owner",
"directRole": null,
"inherited": false,
"resourceId": "0c6a8f52-3d7e-4b19-9a4e-6f1d2c8b7e30",
"addedAt": "2026-10-01T08:30:12.418Z",
"avatarUrl": null
}
],
"role": "owner"
}API 레퍼런스
Webhooks
Outgoing webhook subscriptions (REST hooks: subscribe/unsubscribe).
- GET/api/webhooks
- POST/api/webhooks
- GET/api/webhooks/events
- GET/api/webhooks/{webhookId}
- PATCH/api/webhooks/{webhookId}
- DELETE/api/webhooks/{webhookId}
- GET/api/webhooks/{webhookId}/deliveries
- GET/api/webhooks/{webhookId}/deliveries/{deliveryId}
- POST/api/webhooks/{webhookId}/deliveries/{deliveryId}/redeliver
- POST/api/webhooks/{webhookId}/test
- POST/api/webhooks/{webhookId}/rotate-secret
Webhook
속성
| 필드 | 유형 | 설명 | |||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|
| id | string (uuid) | ||||||||||
| workspaceId | string (uuid) | ||||||||||
| workspaceName | string | ||||||||||
| projectId | string (uuid)null 허용 | null = every project of the workspace. | |||||||||
| projectName | stringnull 허용 | ||||||||||
| name | string | ||||||||||
| url | string (uri) | ||||||||||
| events | array of string | 다음 중 하나: | |||||||||
| secretPrefix | string | First characters of the signing secret (not secret). | |||||||||
| active | boolean | ||||||||||
| disabledReason | stringnull 허용 | 다음 중 하나: | |||||||||
| disabledAt | string (date-time)null 허용 | ||||||||||
| consecutiveFailures | integer | ||||||||||
| viaApi | boolean | Created with the API (REST hook) rather than from the profile. | |||||||||
| createdBy | objectnull 허용 | createdBy의 필드 표시
| |||||||||
| createdAt | string (date-time) | ||||||||||
| updatedAt | string (date-time) | ||||||||||
| lastDeliveryAt | string (date-time)null 허용 | ||||||||||
| lastSuccessAt | string (date-time)null 허용 |
{
"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
속성
| 필드 | 유형 | 설명 |
|---|---|---|
| id | string (uuid) | |
| eventId | string (uuid) | |
| eventType | string | |
| status | string | 다음 중 하나: |
| attempts | integer | |
| nextAttemptAt | string (date-time)null 허용 | |
| lastAttemptAt | string (date-time)null 허용 | |
| responseStatus | integernull 허용 | |
| responseExcerpt | stringnull 허용 | |
| error | stringnull 허용 | |
| durationMs | integernull 허용 | |
| manual | boolean | |
| createdAt | string (date-time) |
{
"id": "4b207161-80f9-40bb-bc3f-d8f70315be00",
"eventId": "6f1c1d1e-2f4b-4d0a-9a77-2a3c5b1e9f10",
"eventType": "task.completed",
"status": "success",
"attempts": 1,
"nextAttemptAt": null,
"lastAttemptAt": "2026-10-01T08:30:12.418Z",
"responseStatus": 200,
"responseExcerpt": "{\"ok\":true}",
"error": null,
"durationMs": 39,
"manual": false,
"createdAt": "2026-10-01T08:30:12.418Z"
}Event
속성
| 필드 | 유형 | 설명 | |||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|
| id 필수 | string (uuid) | Event id, also in the Planitude-Event-Id header: use it to deduplicate. | |||||||||
| type 필수 | string | 다음 중 하나: | |||||||||
| apiVersion 필수 | string | 다음 중 하나: | |||||||||
| createdAt 필수 | string (date-time) | ||||||||||
| workspaceId 필수 | string (uuid) | ||||||||||
| projectId | string (uuid)null 허용 | ||||||||||
| actor | objectnull 허용 | Who made the change (null for system changes).actor의 필드 표시
| |||||||||
| data 필수 | object |
{
"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)
Compact project representation used in webhook payloads.
속성
| 필드 | 유형 | 설명 |
|---|---|---|
| id | string (uuid) | |
| workspaceId | string (uuid) | |
| key | string | |
| name | string | |
| description | stringnull 허용 | |
| startDate | string (date) | |
| deadline | string (date)null 허용 | |
| currency | string | |
| scheduleFrom | string | 다음 중 하나: |
| color | stringnull 허용 | |
| archived | boolean | |
| createdAt | string (date-time) | |
| updatedAt | string (date-time) | |
| url | string (uri) |
{
"id": "40be00e0-7e34-4f7a-864b-5cc986f575b8",
"workspaceId": "824650d2-d0b3-4cac-8df8-5743ae9ff65a",
"key": "WHX",
"name": "Warehouse expansion",
"description": null,
"startDate": "2026-10-05",
"deadline": null,
"currency": "EUR",
"scheduleFrom": "forward",
"color": null,
"archived": false,
"createdAt": "2026-10-01T08:30:12.418Z",
"updatedAt": "2026-10-01T08:30:12.418Z",
"url": "https://planitude.app/app/projects/40be00e0-7e34-4f7a-864b-5cc986f575b8"
}List webhook subscriptions
GET/api/webhooks
읽기 권한이 있는 키
쿼리 매개변수
| 필드 | 유형 | 설명 |
|---|---|---|
| workspaceId | string (uuid) | |
| projectId | string (uuid) |
반환값
200 OK Subscriptions
| 필드 | 유형 | 설명 |
|---|---|---|
| webhooks | array of Webhook |
curl https://planitude.app/api/webhooks \
-H "Authorization: Bearer $PLANITUDE_API_KEY"const res = await fetch("https://planitude.app/api/webhooks", {
headers: {
Authorization: `Bearer ${process.env.PLANITUDE_API_KEY}`,
},
});
if (!res.ok) throw new Error(`${res.status} ${await res.text()}`);
console.log(await res.json());import os
import requests
res = requests.get(
"https://planitude.app/api/webhooks",
headers={"Authorization": f"Bearer {os.environ['PLANITUDE_API_KEY']}"},
)
res.raise_for_status()
print(res.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
쓰기 권한이 있는 키
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.
요청 본문
| 필드 | 유형 | 설명 |
|---|---|---|
| url 필수 | string (uri) | HTTPS endpoint that receives the events. Public addresses only. |
| events 필수 | array of string | * = all events.다음 중 하나: |
| name | string | |
| projectId | string (uuid) | Limit to one project (editor role needed). Omit for the whole workspace (owner/admin). |
| workspaceId | string (uuid) |
반환값
201 Created Created
| 필드 | 유형 | 설명 |
|---|---|---|
| webhook | Webhook | |
| secret | string | whsec_... |
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"
}'const res = await fetch("https://planitude.app/api/webhooks", {
method: "POST",
headers: {
Authorization: `Bearer ${process.env.PLANITUDE_API_KEY}`,
"Content-Type": "application/json",
},
body: JSON.stringify({
url: "https://example.com/hooks/planitude",
events: ["task.created", "task.completed"],
projectId: "40be00e0-7e34-4f7a-864b-5cc986f575b8",
}),
});
if (!res.ok) throw new Error(`${res.status} ${await res.text()}`);
console.log(await res.json());import os
import requests
res = requests.post(
"https://planitude.app/api/webhooks",
headers={"Authorization": f"Bearer {os.environ['PLANITUDE_API_KEY']}"},
json={
"url": "https://example.com/hooks/planitude",
"events": ["task.created", "task.completed"],
"projectId": "40be00e0-7e34-4f7a-864b-5cc986f575b8",
},
)
res.raise_for_status()
print(res.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
공개, 키 불필요
반환값
200 OK Events
| 필드 | 유형 | 설명 |
|---|---|---|
| apiVersion | string | |
| events | array of string |
curl https://planitude.app/api/webhooks/eventsconst res = await fetch("https://planitude.app/api/webhooks/events");
if (!res.ok) throw new Error(`${res.status} ${await res.text()}`);
console.log(await res.json());import requests
res = requests.get("https://planitude.app/api/webhooks/events")
res.raise_for_status()
print(res.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}
읽기 권한이 있는 키
경로 매개변수
| 필드 | 유형 | 설명 |
|---|---|---|
| webhookId 필수 | string (uuid) | Webhook id. |
반환값
200 OK Subscription
| 필드 | 유형 | 설명 |
|---|---|---|
| webhook | Webhook |
curl https://planitude.app/api/webhooks/1747ee7f-a395-4d3c-94b0-54b513d71d88 \
-H "Authorization: Bearer $PLANITUDE_API_KEY"const res = await fetch("https://planitude.app/api/webhooks/1747ee7f-a395-4d3c-94b0-54b513d71d88", {
headers: {
Authorization: `Bearer ${process.env.PLANITUDE_API_KEY}`,
},
});
if (!res.ok) throw new Error(`${res.status} ${await res.text()}`);
console.log(await res.json());import os
import requests
res = requests.get(
"https://planitude.app/api/webhooks/1747ee7f-a395-4d3c-94b0-54b513d71d88",
headers={"Authorization": f"Bearer {os.environ['PLANITUDE_API_KEY']}"},
)
res.raise_for_status()
print(res.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}
쓰기 권한이 있는 키
경로 매개변수
| 필드 | 유형 | 설명 |
|---|---|---|
| webhookId 필수 | string (uuid) | Webhook id. |
요청 본문
| 필드 | 유형 | 설명 |
|---|---|---|
| name | string | |
| url | string (uri) | |
| events | array of string | |
| active | boolean | false = pause; true = resume (also resets the failure counter). |
반환값
200 OK Updated
| 필드 | 유형 | 설명 |
|---|---|---|
| webhook | Webhook |
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}'const res = await fetch("https://planitude.app/api/webhooks/1747ee7f-a395-4d3c-94b0-54b513d71d88", {
method: "PATCH",
headers: {
Authorization: `Bearer ${process.env.PLANITUDE_API_KEY}`,
"Content-Type": "application/json",
},
body: JSON.stringify({
active: false,
}),
});
if (!res.ok) throw new Error(`${res.status} ${await res.text()}`);
console.log(await res.json());import os
import requests
res = requests.patch(
"https://planitude.app/api/webhooks/1747ee7f-a395-4d3c-94b0-54b513d71d88",
headers={"Authorization": f"Bearer {os.environ['PLANITUDE_API_KEY']}"},
json={
"active": False,
},
)
res.raise_for_status()
print(res.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)
DEL/api/webhooks/{webhookId}
쓰기 권한이 있는 키
경로 매개변수
| 필드 | 유형 | 설명 |
|---|---|---|
| webhookId 필수 | string (uuid) | Webhook id. |
반환값
204 No Content Deleted
curl -X DELETE https://planitude.app/api/webhooks/1747ee7f-a395-4d3c-94b0-54b513d71d88 \
-H "Authorization: Bearer $PLANITUDE_API_KEY"const res = await fetch("https://planitude.app/api/webhooks/1747ee7f-a395-4d3c-94b0-54b513d71d88", {
method: "DELETE",
headers: {
Authorization: `Bearer ${process.env.PLANITUDE_API_KEY}`,
},
});
if (!res.ok) throw new Error(`${res.status} ${await res.text()}`);
console.log(res.status); // 204import os
import requests
res = requests.delete(
"https://planitude.app/api/webhooks/1747ee7f-a395-4d3c-94b0-54b513d71d88",
headers={"Authorization": f"Bearer {os.environ['PLANITUDE_API_KEY']}"},
)
res.raise_for_status()
print(res.status_code) # 204Delivery log (newest first, kept 30 days)
GET/api/webhooks/{webhookId}/deliveries
읽기 권한이 있는 키
경로 매개변수
| 필드 | 유형 | 설명 |
|---|---|---|
| webhookId 필수 | string (uuid) | Webhook id. |
쿼리 매개변수
| 필드 | 유형 | 설명 |
|---|---|---|
| limit | integer | 1-100. 기본값: |
| before | string (date-time) | nextCursor of the previous page. |
반환값
200 OK Deliveries
| 필드 | 유형 | 설명 |
|---|---|---|
| deliveries | array of Delivery | |
| nextCursor | stringnull 허용 |
curl https://planitude.app/api/webhooks/1747ee7f-a395-4d3c-94b0-54b513d71d88/deliveries \
-H "Authorization: Bearer $PLANITUDE_API_KEY"const res = await fetch("https://planitude.app/api/webhooks/1747ee7f-a395-4d3c-94b0-54b513d71d88/deliveries", {
headers: {
Authorization: `Bearer ${process.env.PLANITUDE_API_KEY}`,
},
});
if (!res.ok) throw new Error(`${res.status} ${await res.text()}`);
console.log(await res.json());import os
import requests
res = requests.get(
"https://planitude.app/api/webhooks/1747ee7f-a395-4d3c-94b0-54b513d71d88/deliveries",
headers={"Authorization": f"Bearer {os.environ['PLANITUDE_API_KEY']}"},
)
res.raise_for_status()
print(res.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}
읽기 권한이 있는 키
경로 매개변수
| 필드 | 유형 | 설명 |
|---|---|---|
| webhookId 필수 | string (uuid) | Webhook id. |
| deliveryId 필수 | string (uuid) |
반환값
200 OK Payload
| 필드 | 유형 | 설명 |
|---|---|---|
| payload | Event |
curl https://planitude.app/api/webhooks/1747ee7f-a395-4d3c-94b0-54b513d71d88/deliveries/4b207161-80f9-40bb-bc3f-d8f70315be00 \
-H "Authorization: Bearer $PLANITUDE_API_KEY"const res = await fetch("https://planitude.app/api/webhooks/1747ee7f-a395-4d3c-94b0-54b513d71d88/deliveries/4b207161-80f9-40bb-bc3f-d8f70315be00", {
headers: {
Authorization: `Bearer ${process.env.PLANITUDE_API_KEY}`,
},
});
if (!res.ok) throw new Error(`${res.status} ${await res.text()}`);
console.log(await res.json());import os
import requests
res = requests.get(
"https://planitude.app/api/webhooks/1747ee7f-a395-4d3c-94b0-54b513d71d88/deliveries/4b207161-80f9-40bb-bc3f-d8f70315be00",
headers={"Authorization": f"Bearer {os.environ['PLANITUDE_API_KEY']}"},
)
res.raise_for_status()
print(res.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
쓰기 권한이 있는 키
경로 매개변수
| 필드 | 유형 | 설명 |
|---|---|---|
| webhookId 필수 | string (uuid) | Webhook id. |
| deliveryId 필수 | string (uuid) |
반환값
202 Accepted Queued
| 필드 | 유형 | 설명 |
|---|---|---|
| delivery | Delivery |
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"const res = await fetch("https://planitude.app/api/webhooks/1747ee7f-a395-4d3c-94b0-54b513d71d88/deliveries/4b207161-80f9-40bb-bc3f-d8f70315be00/redeliver", {
method: "POST",
headers: {
Authorization: `Bearer ${process.env.PLANITUDE_API_KEY}`,
},
});
if (!res.ok) throw new Error(`${res.status} ${await res.text()}`);
console.log(await res.json());import os
import requests
res = requests.post(
"https://planitude.app/api/webhooks/1747ee7f-a395-4d3c-94b0-54b513d71d88/deliveries/4b207161-80f9-40bb-bc3f-d8f70315be00/redeliver",
headers={"Authorization": f"Bearer {os.environ['PLANITUDE_API_KEY']}"},
)
res.raise_for_status()
print(res.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
쓰기 권한이 있는 키
경로 매개변수
| 필드 | 유형 | 설명 |
|---|---|---|
| webhookId 필수 | string (uuid) | Webhook id. |
반환값
202 Accepted Queued
| 필드 | 유형 | 설명 |
|---|---|---|
| delivery | Delivery |
curl -X POST https://planitude.app/api/webhooks/1747ee7f-a395-4d3c-94b0-54b513d71d88/test \
-H "Authorization: Bearer $PLANITUDE_API_KEY"const res = await fetch("https://planitude.app/api/webhooks/1747ee7f-a395-4d3c-94b0-54b513d71d88/test", {
method: "POST",
headers: {
Authorization: `Bearer ${process.env.PLANITUDE_API_KEY}`,
},
});
if (!res.ok) throw new Error(`${res.status} ${await res.text()}`);
console.log(await res.json());import os
import requests
res = requests.post(
"https://planitude.app/api/webhooks/1747ee7f-a395-4d3c-94b0-54b513d71d88/test",
headers={"Authorization": f"Bearer {os.environ['PLANITUDE_API_KEY']}"},
)
res.raise_for_status()
print(res.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
쓰기 권한이 있는 키
경로 매개변수
| 필드 | 유형 | 설명 |
|---|---|---|
| webhookId 필수 | string (uuid) | Webhook id. |
반환값
200 OK New secret (shown once)
| 필드 | 유형 | 설명 |
|---|---|---|
| webhook | Webhook | |
| secret | string |
curl -X POST https://planitude.app/api/webhooks/1747ee7f-a395-4d3c-94b0-54b513d71d88/rotate-secret \
-H "Authorization: Bearer $PLANITUDE_API_KEY"const res = await fetch("https://planitude.app/api/webhooks/1747ee7f-a395-4d3c-94b0-54b513d71d88/rotate-secret", {
method: "POST",
headers: {
Authorization: `Bearer ${process.env.PLANITUDE_API_KEY}`,
},
});
if (!res.ok) throw new Error(`${res.status} ${await res.text()}`);
console.log(await res.json());import os
import requests
res = requests.post(
"https://planitude.app/api/webhooks/1747ee7f-a395-4d3c-94b0-54b513d71d88/rotate-secret",
headers={"Authorization": f"Bearer {os.environ['PLANITUDE_API_KEY']}"},
)
res.raise_for_status()
print(res.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"
}웹훅
웹훅
웹훅은 웹 앱, API, MCP 서버, 가져오기 중 어디에서 변경이 발생하든 변경 즉시 JSON 본문이 담긴 HTTPS POST를 엔드포인트로 보냅니다. 웹훅은 하나의 프로젝트 또는 워크스페이스의 모든 프로젝트(소유자 및 관리자)를 대상으로 합니다.
프로필 → 웹훅에서 또는 API로 REST 훅 방식으로 생성합니다. POST /api/webhooks로 구독하고 DELETE로 구독을 해제합니다. 서명 시크릿은 한 번만 표시됩니다.
모든 요청에는 다음 헤더가 포함됩니다.
| 헤더 | 설명 |
|---|---|
| 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 |
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"
}'const res = await fetch("https://planitude.app/api/webhooks", {
method: "POST",
headers: {
Authorization: `Bearer ${process.env.PLANITUDE_API_KEY}`,
"Content-Type": "application/json",
},
body: JSON.stringify({
url: "https://example.com/hooks/planitude",
events: ["task.created", "task.completed"],
projectId: "40be00e0-7e34-4f7a-864b-5cc986f575b8",
}),
});
if (!res.ok) throw new Error(`${res.status} ${await res.text()}`);
console.log(await res.json());import os
import requests
res = requests.post(
"https://planitude.app/api/webhooks",
headers={"Authorization": f"Bearer {os.environ['PLANITUDE_API_KEY']}"},
json={
"url": "https://example.com/hooks/planitude",
"events": ["task.created", "task.completed"],
"projectId": "40be00e0-7e34-4f7a-864b-5cc986f575b8",
},
)
res.raise_for_status()
print(res.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"
}이벤트 유형
개별 이벤트 또는 모든 이벤트를 의미하는 *를 구독하세요. 모든 이벤트의 봉투(envelope) 구조는 같으며 data는 유형에 따라 달라집니다.
| 이벤트 | 설명 |
|---|---|
| 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. |
{
"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"
}
}{
"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"
}
}{
"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"
}
}
}{
"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"
}
}
}{
"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
}
}
}
}{
"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"
}
}
}{
"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"
}
}
}{
"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"
}
}
}
}{
"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
}
}
}{
"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
}
}
}{
"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
}
}
}{
"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
}
}
}{
"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"
}
}
}{
"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"
}
}
}서명 검증
모든 요청은 HMAC-SHA256과 웹훅 시크릿으로 서명됩니다. Planitude-Signature 헤더에는 타임스탬프 t와 서명 v1이 들어 있으며, 서명은 t, 마침표, 원본 본문을 이어 붙여 계산합니다.
- JSON을 파싱하기 전의 원본 본문을 사용하세요. 다시 직렬화한 본문은 서명이 다릅니다.
- HMAC를 계산하고 상수 시간 비교로 대조하세요.
- 타임스탬프가 시스템 시계와 5분 넘게 차이 나는 요청은 거부하세요. 가로챈 요청을 재전송할 수 없습니다.
- 시크릿은 원할 때 언제든 교체할 수 있으며, 이전 시크릿은 즉시 사용할 수 없게 됩니다.
import { createHmac, timingSafeEqual } from "node:crypto";
// rawBody: the request body as a string, before JSON.parse
export function verifyPlanitude(rawBody, header, secret, toleranceSec = 300) {
const parts = Object.fromEntries(header.split(",").map((p) => p.split("=")));
const t = Number(parts.t);
if (!t || Math.abs(Date.now() / 1000 - t) > toleranceSec) return false; // replay
const expected = createHmac("sha256", secret).update(`${t}.${rawBody}`).digest();
const given = Buffer.from(parts.v1 ?? "", "hex");
return given.length === expected.length && timingSafeEqual(given, expected);
}
// Express: app.post("/hooks/planitude", express.raw({ type: "application/json" }), (req, res) => {
// if (!verifyPlanitude(req.body.toString("utf8"), req.get("Planitude-Signature"), process.env.PLANITUDE_WEBHOOK_SECRET)) return res.sendStatus(400);
// res.sendStatus(200); // then process the event in a queue
// });import hashlib
import hmac
import time
def verify_planitude(raw_body: bytes, header: str, secret: str, tolerance: int = 300) -> bool:
parts = dict(p.split("=", 1) for p in header.split(","))
t = int(parts.get("t", "0"))
if abs(time.time() - t) > tolerance:
return False # replay
expected = hmac.new(secret.encode(), f"{t}.".encode() + raw_body, hashlib.sha256).hexdigest()
return hmac.compare_digest(expected, parts.get("v1", ""))
# Flask: verify_planitude(request.get_data(), request.headers["Planitude-Signature"], os.environ["PLANITUDE_WEBHOOK_SECRET"])재시도 및 전달
10초 이내에 아무 2xx로 응답하고 실제 작업은 이후 큐에서 처리하세요. 리디렉션은 따라가지 않습니다. 오류나 시간 초과가 발생하면 Planitude는 총 6회까지 시도합니다. 즉시 시도한 뒤 1 min, 5 min, 30 min, 2 h, 12 h 후에 재시도합니다.
전달은 최소 한 번이며 순서가 보장되지 않습니다. 이벤트 ID(Planitude-Event-Id, 본문의 id와 동일)를 저장해 이미 처리한 이벤트는 건너뛰고, 순서는 createdAt 기준으로 정렬하세요.
5개 이벤트가 연속으로 모든 시도에 실패하면 웹훅이 비활성화되고 소유자에게 이메일이 발송됩니다. 410 Gone 응답은 즉시 비활성화합니다. 프로필에서, 또는 PATCH와 active: true로 다시 활성화할 수 있습니다.
전달 로그(상태, HTTP 코드, 응답 일부, 페이로드, 수동 재전송)는 30일간 보관됩니다. 대상 주소는 공개 주소만 허용됩니다.
시도 1 즉시
시도 2 + 1 min
시도 3 + 5 min
시도 4 + 30 min
시도 5 + 2 h
시도 6 + 12 h엔드포인트 테스트
- 테스트 엔드포인트 또는 프로필의 테스트 버튼으로
webhook.ping이벤트를 보내세요. - 내 컴퓨터에서 이벤트를 받으려면 터널(예: ngrok 또는 Cloudflare Tunnel)로 로컬 서버를 공개하고 그 공개 HTTPS 주소를 구독하세요.
- 전달 로그를 열어 상태, HTTP 코드, 응답을 확인하고, 엔드포인트를 수정한 뒤 전달을 다시 보내세요.
curl -X POST https://planitude.app/api/webhooks/1747ee7f-a395-4d3c-94b0-54b513d71d88/test \
-H "Authorization: Bearer $PLANITUDE_API_KEY"const res = await fetch("https://planitude.app/api/webhooks/1747ee7f-a395-4d3c-94b0-54b513d71d88/test", {
method: "POST",
headers: {
Authorization: `Bearer ${process.env.PLANITUDE_API_KEY}`,
},
});
if (!res.ok) throw new Error(`${res.status} ${await res.text()}`);
console.log(await res.json());import os
import requests
res = requests.post(
"https://planitude.app/api/webhooks/1747ee7f-a395-4d3c-94b0-54b513d71d88/test",
headers={"Authorization": f"Bearer {os.environ['PLANITUDE_API_KEY']}"},
)
res.raise_for_status()
print(res.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"
}
}AI 및 MCP
MCP 서버
Planitude는 MCP(Model Context Protocol) 서버입니다. AI 어시스턴트와 코드 편집기가 사용자의 권한 범위 내에서 프로젝트를 읽고, 크리티컬 패스와 작업량을 분석하며, 작업을 생성하거나 변경합니다. 웹 앱은 OAuth(로그인 후 접근 허용)로 연결하고, 명령줄 도구와 편집기는 API 키를 사용합니다.
서버 URL
https://planitude.app/mcp
- ChatGPTOAuth
ChatGPT에서 설정 › 보안 및 로그인을 열고 개발자 모드를 켜세요. 서버 URL과 OAuth 인증으로 앱을 만든 뒤 Planitude에 로그인하고 접근을 허용합니다.
공식 문서 ↗ - ClaudeOAuth
맞춤 설정 › 커넥터 › 사용자 지정 커넥터 추가를 열고 서버 URL을 붙여넣은 뒤 연결을 선택하세요. Planitude가 열리면 로그인하고 접근을 허용합니다. claude.ai, Claude Desktop, 모바일 앱에서 작동합니다.
공식 문서 ↗ - GeminiOAuth
gemini.google.com에서 설정 › 연결된 앱을 열고 사용자 지정 앱 추가를 선택해 서버 URL을 붙여넣으세요. 그런 다음 Planitude에 로그인하고 접근을 허용합니다.
공식 문서 ↗ - Claude CodeAPI 키
키를 환경 변수에 넣고 명령을 실행하세요. 모든 프로젝트에서 사용하려면
공식 문서 ↗--scope user를 추가하고claude mcp list로 확인합니다. - CodexAPI 키
키를
공식 문서 ↗PLANITUDE_API_KEY에 넣고~/.codex/config.toml에 블록을 추가하세요. CLI, 앱, IDE 확장 프로그램 모두에 적용됩니다. - CursorAPI 키
공식 문서 ↗~/.cursor/mcp.json(모든 프로젝트) 또는.cursor/mcp.json(단일 프로젝트)에 블록을 추가하세요. Cursor는 환경 변수에서 키를 읽습니다. - Visual Studio CodeAPI 키
워크스페이스에
공식 문서 ↗.vscode/mcp.json을 추가하세요. VS Code가 키를 한 번 묻고 파일 밖에 안전하게 저장합니다. - Gemini CLIAPI 키
키를 환경 변수에 넣고 명령을 실행하거나,
공식 문서 ↗~/.gemini/settings.json에 블록을 추가하세요.
도구
서버는 읽기와 분석부터 안전한 변경까지 29개의 도구를 제공합니다. 프로젝트나 작업을 삭제하는 도구는 없습니다.
read
planitude_list_projectsplanitude_portfolio_overviewplanitude_get_projectplanitude_critical_pathplanitude_delay_impactplanitude_monte_carloplanitude_workloadplanitude_list_baselinesplanitude_list_versionsplanitude_list_templatesplanitude_list_commentsplanitude_preview_import
write
planitude_create_projectplanitude_create_project_from_templateplanitude_create_project_from_planplanitude_import_fileplanitude_edit_projectplanitude_add_taskplanitude_update_taskplanitude_set_progressplanitude_indent_taskplanitude_outdent_taskplanitude_link_tasksplanitude_unlink_tasksplanitude_rescheduleplanitude_configure_simulationplanitude_add_commentplanitude_save_baselineplanitude_save_version
Server URL
https://planitude.app/mcpServer URL
https://planitude.app/mcpServer URL
https://planitude.app/mcpTerminal
claude mcp add --transport http planitude https://planitude.app/mcp \
--header "Authorization: Bearer $PLANITUDE_API_KEY"~/.codex/config.toml
[mcp_servers.planitude]
url = "https://planitude.app/mcp"
bearer_token_env_var = "PLANITUDE_API_KEY"~/.cursor/mcp.json
{
"mcpServers": {
"planitude": {
"url": "https://planitude.app/mcp",
"headers": {
"Authorization": "Bearer ${env:PLANITUDE_API_KEY}"
}
}
}
}.vscode/mcp.json
{
"servers": {
"planitude": {
"type": "http",
"url": "https://planitude.app/mcp",
"headers": {
"Authorization": "Bearer ${input:planitude-key}"
}
}
},
"inputs": [
{
"id": "planitude-key",
"type": "promptString",
"description": "Planitude API key",
"password": true
}
]
}Terminal
gemini mcp add --transport http \
--header "Authorization: Bearer $PLANITUDE_API_KEY" planitude https://planitude.app/mcp~/.gemini/settings.json
{
"mcpServers": {
"planitude": {
"httpUrl": "https://planitude.app/mcp",
"headers": {
"Authorization": "Bearer $PLANITUDE_API_KEY"
}
}
}
}기본 제공 연동
코드가 필요 없습니다. 이러한 연동은 동일한 API와 웹훅을 사용하며 몇 번의 클릭으로 설정할 수 있습니다.
- Jira사용 가능작업, 날짜, 종속 관계를 Jira 이슈와 양방향으로 동기화합니다.
- Azure DevOps사용 가능Azure DevOps Boards 작업 항목과 양방향으로 동기화합니다.
- Slack사용 가능채널에서 프로젝트 업데이트, 일일 요약, 슬래시 명령을 사용합니다.
- Microsoft Teams사용 가능Microsoft Teams에서 업데이트, 요약, Planitude 탭을 사용합니다.
- Zapier, Make, n8n출시 예정노코드 자동화를 위한 작업 및 프로젝트의 즉시 트리거와 액션.
변경 이력
OpenAPI 문서에서 생성한 새로운 개발자 문서. OpenAPI 문서에 구성원, 댓글, 기준선, 템플릿, 담당자에 대해 반환되는 모든 필드가 설명되었습니다.
API 버전 2026-10-01: 서명된 발신 웹훅, 문서화된 공개 REST API(OpenAPI 3.1), 커서 페이지 매김, externalId 검색.
MCP 서버와 API용 OAuth 2.1: ChatGPT, Claude, Gemini가 키를 복사하지 않고 연결됩니다.
MCP 서버와 읽기 또는 읽기+쓰기 권한의 개인 API 키.