API Endpoints
This is the complete REST API reference for IntuitivePM. All endpoints require authentication via a Bearer token (JWT or API key) in the Authorization header. See API Authentication for setup instructions.
Base URL: https://api.intuitivepm.net/api
Resource IDs are UUIDs. Task effort is tracked with estimatedHours and loggedHours — the Sprint Wizard presents estimatedHours as story points, but there is no separate storyPoints field on a task.
Projects
List All Projects
Retrieve all projects accessible to the authenticated user.
GET /projects
Response: 200 OK
[
{
"id": "3f9a1c2e-5b7d-4e81-9a3c-1d2e4f6a8b0c",
"name": "Website Redesign",
"description": "Complete overhaul of the marketing website",
"status": "active",
"teamId": "1a2b3c4d-5e6f-4a7b-8c9d-0e1f2a3b4c5d",
"createdAt": "2026-01-15T10:30:00.000Z",
"updatedAt": "2026-02-20T14:22:00.000Z"
}
]
Create a Project
POST /projects
Request Body:
{
"name": "Mobile App v2",
"description": "Native mobile application rebuild",
"teamId": "1a2b3c4d-5e6f-4a7b-8c9d-0e1f2a3b4c5d"
}
Response: 201 Created
{
"id": "5c6d7e8f-9a0b-4c1d-8e2f-3a4b5c6d7e8f",
"name": "Mobile App v2",
"description": "Native mobile application rebuild",
"status": "active",
"teamId": "1a2b3c4d-5e6f-4a7b-8c9d-0e1f2a3b4c5d",
"createdAt": "2026-02-22T09:00:00.000Z",
"updatedAt": "2026-02-22T09:00:00.000Z"
}
Get a Project
GET /projects/{id}
Response: 200 OK
{
"id": "3f9a1c2e-5b7d-4e81-9a3c-1d2e4f6a8b0c",
"name": "Website Redesign",
"description": "Complete overhaul of the marketing website",
"status": "active",
"teamId": "1a2b3c4d-5e6f-4a7b-8c9d-0e1f2a3b4c5d",
"members": [
{
"id": "8c1d2e3f-4a5b-4c7d-8e9f-0a1b2c3d4e5f",
"name": "Jane Smith",
"role": "ADMIN"
}
],
"createdAt": "2026-01-15T10:30:00.000Z",
"updatedAt": "2026-02-20T14:22:00.000Z"
}
Update a Project
PUT /projects/{id}
Request Body:
{
"name": "Website Redesign v2",
"description": "Updated project scope with mobile-first approach"
}
Response: 200 OK
Returns the updated project object.
Delete a Project
DELETE /projects/{id}
Response: 204 No Content
Common Errors — Projects
// 401 Unauthorized
{ "error": "Invalid or expired token" }
// 403 Forbidden
{ "error": "Insufficient permissions for this resource" }
// 404 Not Found
{ "error": "Project not found" }
// 400 Bad Request — validation failed
{
"success": false,
"error": {
"code": "VALIDATION_ERROR",
"message": "Invalid input data",
"details": { "name": "Required" },
"traceId": "5f2b9c81-7d3e-4a06-9c1b-2e8d4a7f0b13"
}
}
See Error Responses for the full envelope and status-code table.
Tasks
List Tasks
Retrieve tasks in your company, optionally narrowed to one or more projects. Tasks are listed from the top-level /tasks collection and filtered with query parameters — there is no /projects/{id}/tasks sub-collection.
GET /tasks?projectId={id}
Query Parameters:
Parameters marked multi accept a comma-separated list (for example status=To Do,In Progress).
| Parameter | Type | Description |
|---|---|---|
projectId | string (multi) | Filter by project ID |
status | string (multi) | Filter by workflow status (e.g., Backlog, To Do, In Progress, In Review, Done, Blocked) |
priority | string (multi) | Filter by priority (None, Low, Medium, High, Urgent) |
assigneeId | string (multi) | Filter by assigned user ID |
sprintId | string | Filter by sprint ID |
epicId | string (multi) | Filter by epic ID |
labelIds | string (multi) | Filter by label IDs |
tid | string (multi) | Filter by human-readable task ID |
isBlocked | boolean | Only blocked (true) or unblocked (false) tasks |
search | string | Free-text search across task fields |
dueBefore / dueAfter | string | ISO 8601 due-date bounds |
fields | string | Sparse fieldset — comma-separated list of fields to return |
include | string | Include related data (assignee, project) |
sortBy | string | createdAt, updatedAt, dueDate, priority, status, title, or rank |
sortOrder | string | asc or desc (default desc) |
Note: Status is a free-form string so companies can define custom workflow lanes. The values above are the defaults — your workspace may use different names.
Response: 200 OK
[
{
"id": "7a8b9c0d-1e2f-4a3b-8c5d-6e7f8a9b0c1d",
"title": "Design homepage mockup",
"description": "Create Figma mockups for the new homepage layout",
"status": "In Progress",
"priority": "High",
"estimatedHours": 5,
"loggedHours": 2,
"projectId": "3f9a1c2e-5b7d-4e81-9a3c-1d2e4f6a8b0c",
"assigneeId": "8c1d2e3f-4a5b-4c7d-8e9f-0a1b2c3d4e5f",
"sprintId": "b2c3d4e5-f6a7-4b8c-9d0e-1f2a3b4c5d6e",
"epicId": "d4e5f6a7-b8c9-4d0e-8f2a-3b4c5d6e7f80",
"dueDate": "2026-02-28T00:00:00.000Z",
"createdAt": "2026-02-01T08:00:00.000Z",
"updatedAt": "2026-02-20T16:45:00.000Z"
}
]
Create a Task
Tasks are created on the top-level collection; the target project is given by projectId in the body.
POST /tasks
Request Body:
title and projectId are required. status defaults to Backlog and priority to None.
{
"title": "Implement user authentication",
"description": "Add JWT-based login and registration flows",
"status": "To Do",
"priority": "High",
"projectId": "3f9a1c2e-5b7d-4e81-9a3c-1d2e4f6a8b0c",
"assigneeId": "8c1d2e3f-4a5b-4c7d-8e9f-0a1b2c3d4e5f",
"sprintId": "b2c3d4e5-f6a7-4b8c-9d0e-1f2a3b4c5d6e",
"epicId": "d4e5f6a7-b8c9-4d0e-8f2a-3b4c5d6e7f80",
"dueDate": "2026-03-01T00:00:00.000Z"
}
Response: 201 Created
Returns the created task object.
Get a Task
GET /tasks/{id}
Response: 200 OK
{
"id": "7a8b9c0d-1e2f-4a3b-8c5d-6e7f8a9b0c1d",
"title": "Design homepage mockup",
"description": "Create Figma mockups for the new homepage layout",
"status": "In Progress",
"priority": "High",
"estimatedHours": 5,
"loggedHours": 2,
"projectId": "3f9a1c2e-5b7d-4e81-9a3c-1d2e4f6a8b0c",
"assigneeId": "8c1d2e3f-4a5b-4c7d-8e9f-0a1b2c3d4e5f",
"assignee": {
"id": "8c1d2e3f-4a5b-4c7d-8e9f-0a1b2c3d4e5f",
"name": "Jane Smith"
},
"sprintId": "b2c3d4e5-f6a7-4b8c-9d0e-1f2a3b4c5d6e",
"epicId": "d4e5f6a7-b8c9-4d0e-8f2a-3b4c5d6e7f80",
"labels": [
{
"id": "e6f7a8b9-c0d1-4e2f-8a3b-4c5d6e7f8a9b",
"name": "frontend",
"color": "#3B82F6"
}
],
"dueDate": "2026-02-28T00:00:00.000Z",
"createdAt": "2026-02-01T08:00:00.000Z",
"updatedAt": "2026-02-20T16:45:00.000Z"
}
Update a Task
PUT /tasks/{id}
Request Body:
{
"status": "Done",
"priority": "Medium"
}
Response: 200 OK
Returns the updated task object. Only include fields you want to change.
Delete a Task
DELETE /tasks/{id}
Response: 204 No Content
Common Errors — Tasks
// 401 Unauthorized
{ "error": "Invalid or expired token" }
// 403 Forbidden
{ "error": "Insufficient permissions for this resource" }
// 404 Not Found
{ "error": "Task not found" }
// 400 Bad Request — validation failed
{
"success": false,
"error": {
"code": "VALIDATION_ERROR",
"message": "Invalid input data",
"details": { "title": "Required", "projectId": "Invalid uuid" },
"traceId": "5f2b9c81-7d3e-4a06-9c1b-2e8d4a7f0b13"
}
}
See Error Responses for the full envelope and status-code table.
Assign a Task
Assignment is an ordinary field update — there is no separate assign endpoint. Set assigneeId with PUT /tasks/{id}, or clear it by sending null.
PUT /tasks/{id}
Request Body:
{
"assigneeId": "7a8b9c0d-1e2f-4a3b-8c5d-6e7f8a9b0c1d"
}
Response: 200 OK
Returns the updated task object.
Virtual Colleagues
List All Colleagues
Retrieve all Virtual Colleagues in your company.
GET /virtual-colleagues
Response: 200 OK
[
{
"id": "c5d6e7f8-a9b0-4c1d-8e2f-3a4b5c6d7e8f",
"name": "Morning Standup Bot",
"role": "scrum_master",
"description": "Posts daily standup summary to Slack at 8 AM",
"avatar": "bot-pm-01",
"active": true,
"schedule": {
"frequency": "daily",
"time": "08:00",
"timezone": "America/New_York"
},
"lastRunAt": "2026-02-22T13:00:00.000Z",
"lastRunStatus": "success",
"createdAt": "2026-02-01T10:00:00.000Z",
"updatedAt": "2026-02-22T13:00:00.000Z"
}
]
Colleague Roles
Every colleague is created from one of nine built-in roles. The role sets the colleague’s default chase, reminder, escalation, reporting, and tone behaviour, which you can then override per colleague.
| Role value | Shown as | Focus |
|---|---|---|
scrum_master | Scrum Master | Sprint health, blockers, standups |
project_lead | Project Lead | Milestones, resources, delivery |
product_owner | Product Owner | Backlog health, priorities, releases |
manager | Manager | People, workload, performance |
member | Member | Own tasks, deadlines, reviews |
myself | Myself | Personal digest, focus, self-tracking |
customer | Customer | External updates, milestones, delivery |
agent_orchestrator | Agent Orchestrator | Plans, delegates, reviews, coordinates agents |
agent | Agent | Executes assigned task or subtask work |
Retrieve the full preset definitions — including each role’s default settings — from:
GET /virtual-colleagues/presets
Create a Colleague
POST /virtual-colleagues
Request Body:
{
"name": "Backlog Groomer",
"role": "product_owner",
"description": "Flags stale and unprioritised backlog items",
"avatar": "bot-po-01",
"schedule": {
"frequency": "hourly",
"timezone": "UTC"
},
"projectIds": ["3f9a1c2e-5b7d-4e81-9a3c-1d2e4f6a8b0c"],
"notifications": {
"slack": {
"webhookUrl": "https://hooks.slack.com/services/T00/B00/xxxx"
}
}
}
Response: 201 Created
Returns the created colleague object.
Get a Colleague
GET /virtual-colleagues/{id}
Response: 200 OK
Returns the full colleague object including schedule, assigned projects, notification configuration, and recent activity log entries.
Update a Colleague
PUT /virtual-colleagues/{id}
Request Body:
{
"name": "Backlog Groomer v2",
"schedule": {
"frequency": "daily",
"time": "09:00",
"timezone": "UTC"
},
"active": false
}
Response: 200 OK
Returns the updated colleague object. Only include fields you want to change.
Delete a Colleague
DELETE /virtual-colleagues/{id}
Response: 204 No Content
Permanently removes the colleague and its activity history.
Common Errors — Virtual Colleagues
// 400 Bad Request — validation failed
{
"success": false,
"error": {
"code": "VALIDATION_ERROR",
"message": "Invalid input data",
"details": { "role": "Invalid enum value" },
"traceId": "5f2b9c81-7d3e-4a06-9c1b-2e8d4a7f0b13"
}
}
// 404 Not Found
{ "error": "Virtual colleague not found" }
// 409 Conflict — a scan is already running for this colleague
{ "error": "Scan already in progress" }
Run a Colleague Scan Manually
Force an immediate scan outside the colleague’s regular schedule. The scan runs synchronously and the response carries its results, so the request stays open for the duration of the run.
POST /virtual-colleagues/{id}/scan
Response: 200 OK
{
"scanId": "a1b2c3d4-e5f6-4a7b-8c9d-0e1f2a3b4c5d",
"tasksScanned": 142,
"actionsGenerated": 6,
"sent": 6,
"failed": 0,
"durationMs": 1830,
"lastScanAt": "2026-07-31T15:30:00.000Z",
"nextScanAt": "2026-07-31T16:30:00.000Z"
}
A disabled colleague returns 400, and a colleague already mid-scan returns 409.
To run the AI analysis pass instead of the rule-based scan, use POST /virtual-colleagues/{id}/ai-check.
Webhooks
Webhooks let you receive real-time HTTP callbacks when events occur in your projects.
Supported Events
| Event | Description |
|---|---|
task.created | A new task is created |
task.updated | A task is updated |
task.deleted | A task is deleted |
task.completed | A task status changes to Done |
task.assigned | A task is assigned to a user |
task.commented | A comment is added to a task |
project.created | A new project is created |
project.updated | A project is updated |
project.deleted | A project is deleted |
project.archived | A project is archived |
sprint.created | A new sprint is created |
sprint.started | A sprint is started |
sprint.completed | A sprint is completed |
sprint.cancelled | A sprint is cancelled |
team.created | A new team is created |
team.updated | A team is updated |
team.member_added | A member is added to a team |
team.member_removed | A member is removed from a team |
label.created | A new label is created |
label.updated | A label is updated |
label.deleted | A label is deleted |
label.completed | All tasks carrying a label are completed |
epic.created | A new epic is created |
epic.updated | An epic is updated |
epic.deleted | An epic is deleted |
Note: Virtual Colleague runs do not emit webhook events. Use
GET /virtual-colleagues/{id}/scan-historyor the colleague’s activity log to observe runs.
Create a Webhook
POST /webhooks
Request Body:
{
"url": "https://your-server.com/webhooks/intuitivepm",
"events": ["task.created", "task.updated"],
"projectId": "3f9a1c2e-5b7d-4e81-9a3c-1d2e4f6a8b0c",
"secret": "your-webhook-secret"
}
Response: 201 Created
{
"id": "f8a9b0c1-d2e3-4f4a-8b5c-6d7e8f9a0b1c",
"url": "https://your-server.com/webhooks/intuitivepm",
"events": ["task.created", "task.updated"],
"projectId": "3f9a1c2e-5b7d-4e81-9a3c-1d2e4f6a8b0c",
"active": true,
"createdAt": "2026-02-22T10:00:00.000Z"
}
The secret is used to sign webhook payloads. Verify the X-Webhook-Signature header on incoming requests to confirm authenticity — its value is the HMAC-SHA256 of the raw request body, prefixed with sha256=.
List All Webhooks
GET /webhooks
Response: 200 OK
Returns an array of all configured webhooks for your company.
Delete a Webhook
DELETE /webhooks/{id}
Response: 204 No Content
Webhook Payload Format
When an event fires, IntuitivePM sends a POST request to your configured URL with the following structure:
{
"event": "task.updated",
"timestamp": "2026-07-31T14:30:00.000Z",
"data": {
"id": "7a8b9c0d-1e2f-4a3b-8c5d-6e7f8a9b0c1d",
"title": "Design homepage mockup",
"status": "Done"
}
}
Delivery headers:
| Header | Description |
|---|---|
X-Webhook-Signature | sha256= followed by the HMAC-SHA256 of the raw body |
X-Webhook-Event | The event name, matching event in the body |
X-Webhook-Delivery | Unique ID for this delivery attempt |
X-Webhook-Timestamp | ISO 8601 timestamp, matching timestamp in the body |
Any custom headers configured on the webhook are sent alongside these.
Your endpoint must respond with a 2xx status code within 30 seconds. Failed deliveries are retried up to 3 times by default; both the retry count and the delay between attempts are configurable per webhook.
Error Responses
Errors raised by the shared error handler use a structured envelope:
{
"success": false,
"error": {
"code": "VALIDATION_ERROR",
"message": "A human-readable error message",
"details": { "title": "Required" },
"traceId": "5f2b9c81-7d3e-4a06-9c1b-2e8d4a7f0b13"
}
}
details is present only for validation failures. traceId identifies the request in our logs — include it when contacting support.
Note: Not every endpoint routes through the shared handler. Some return a flat
{ "error": "message" }body instead. Treat a non-2xx status code — not the body shape — as the signal that a request failed, and readerrordefensively.
Error Codes
| Status Code | error.code | Common Causes |
|---|---|---|
400 | VALIDATION_ERROR, INVALID_INPUT | Missing required fields, invalid JSON, failed schema validation |
401 | UNAUTHORIZED | Missing or expired token, invalid API key |
403 | FORBIDDEN | Insufficient permissions, or missing the required API-key scope |
404 | NOT_FOUND | Resource does not exist or belongs to another company |
409 | CONFLICT | Conflicting state, such as a scan already in progress |
429 | RATE_LIMIT_EXCEEDED | Rate limit exceeded (see Retry-After header) |
500 | INTERNAL_ERROR, DATABASE_ERROR | Unexpected server failure — contact support if persistent |
503 | SERVICE_UNAVAILABLE | A dependency is temporarily unavailable |
Note: Schema validation failures return
400, not422.
Example Error Responses
400 Validation Error:
{
"success": false,
"error": {
"code": "VALIDATION_ERROR",
"message": "Invalid input data",
"details": { "title": "Required", "projectId": "Invalid uuid" },
"traceId": "5f2b9c81-7d3e-4a06-9c1b-2e8d4a7f0b13"
}
}
429 Rate Limited:
{
"success": false,
"error": {
"code": "RATE_LIMIT_EXCEEDED",
"message": "Too many requests. Please retry after 47 seconds.",
"retryAfter": 47,
"traceId": "9d4c1a77-0b62-4e19-a3f5-7c8b1d2e6f40"
}
}
Rate Limits
The API enforces rate limits to protect service availability for all users.
Limits are applied per minute and selected by how the request is authenticated and which method it uses. Requests authenticated with an API key get their own, much higher allowance — use an API key for automation rather than a user session.
| Tier | Applies to | Default limit |
|---|---|---|
api-key | Any request authenticated with an API key | 2,000 requests/min |
api-read | GET requests | 600 requests/min |
api-write | POST, PUT, PATCH requests | 120 requests/min |
api-delete | DELETE requests | 60 requests/min |
auth | Login, registration, and password-reset endpoints | 200 requests/min |
These are defaults and are configurable per deployment, so treat the response headers — not the table — as authoritative. Health and API-documentation endpoints are exempt.
Rate limit headers returned on every response:
| Header | Description |
|---|---|
X-RateLimit-Limit | Maximum requests allowed in the current window |
X-RateLimit-Remaining | Requests remaining in the current window |
X-RateLimit-Reset | Unix timestamp when the window resets |
X-RateLimit-Policy | Which tier above was applied |
When rate limited (429 Too Many Requests):
{
"success": false,
"error": {
"code": "RATE_LIMIT_EXCEEDED",
"message": "Too many requests. Please retry after 47 seconds.",
"retryAfter": 47,
"traceId": "9d4c1a77-0b62-4e19-a3f5-7c8b1d2e6f40"
}
}
Check the Retry-After response header for the number of seconds to wait before retrying. Implementing exponential backoff in your client is recommended for batch operations.
Pagination
List endpoints use page-based pagination.
Query Parameters:
| Parameter | Type | Default | Description |
|---|---|---|---|
page | integer | 1 | Page number to return |
limit | integer | 50 | Number of items per page (max 200) |
Example:
GET /tasks?projectId=3f9a1c2e-5b7d-4e81-9a3c-1d2e4f6a8b0c&page=1&limit=20
Pagination is opt-in: include the page parameter to get a paginated response. Without it, a list endpoint returns a bare JSON array, which is retained for backward compatibility. With it, the response is wrapped and carries pagination metadata:
{
"success": true,
"data": [ ... ],
"meta": {
"pagination": {
"page": 1,
"limit": 20,
"total": 100,
"totalPages": 5
},
"timestamp": "2026-07-31T14:30:00.000Z"
}
}
Note: Because the response shape changes when
pageis present, pick one style per integration and stick to it rather than addingpageconditionally.
Next Steps
- Set up authentication with the API Authentication guide.
- Configure real-time event delivery with the Webhooks guide.
- Automate workflows using Virtual Colleagues.