H Hypernovi customer docs Documentation space
Browse spaces
Customer documentationAPI Reference

API Endpoints

Complete REST API reference for IntuitivePM.

Maintained by Hypernovi · Updated for the current product release

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).

ParameterTypeDescription
projectIdstring (multi)Filter by project ID
statusstring (multi)Filter by workflow status (e.g., Backlog, To Do, In Progress, In Review, Done, Blocked)
prioritystring (multi)Filter by priority (None, Low, Medium, High, Urgent)
assigneeIdstring (multi)Filter by assigned user ID
sprintIdstringFilter by sprint ID
epicIdstring (multi)Filter by epic ID
labelIdsstring (multi)Filter by label IDs
tidstring (multi)Filter by human-readable task ID
isBlockedbooleanOnly blocked (true) or unblocked (false) tasks
searchstringFree-text search across task fields
dueBefore / dueAfterstringISO 8601 due-date bounds
fieldsstringSparse fieldset — comma-separated list of fields to return
includestringInclude related data (assignee, project)
sortBystringcreatedAt, updatedAt, dueDate, priority, status, title, or rank
sortOrderstringasc 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 valueShown asFocus
scrum_masterScrum MasterSprint health, blockers, standups
project_leadProject LeadMilestones, resources, delivery
product_ownerProduct OwnerBacklog health, priorities, releases
managerManagerPeople, workload, performance
memberMemberOwn tasks, deadlines, reviews
myselfMyselfPersonal digest, focus, self-tracking
customerCustomerExternal updates, milestones, delivery
agent_orchestratorAgent OrchestratorPlans, delegates, reviews, coordinates agents
agentAgentExecutes 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

EventDescription
task.createdA new task is created
task.updatedA task is updated
task.deletedA task is deleted
task.completedA task status changes to Done
task.assignedA task is assigned to a user
task.commentedA comment is added to a task
project.createdA new project is created
project.updatedA project is updated
project.deletedA project is deleted
project.archivedA project is archived
sprint.createdA new sprint is created
sprint.startedA sprint is started
sprint.completedA sprint is completed
sprint.cancelledA sprint is cancelled
team.createdA new team is created
team.updatedA team is updated
team.member_addedA member is added to a team
team.member_removedA member is removed from a team
label.createdA new label is created
label.updatedA label is updated
label.deletedA label is deleted
label.completedAll tasks carrying a label are completed
epic.createdA new epic is created
epic.updatedAn epic is updated
epic.deletedAn epic is deleted

Note: Virtual Colleague runs do not emit webhook events. Use GET /virtual-colleagues/{id}/scan-history or 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:

HeaderDescription
X-Webhook-Signaturesha256= followed by the HMAC-SHA256 of the raw body
X-Webhook-EventThe event name, matching event in the body
X-Webhook-DeliveryUnique ID for this delivery attempt
X-Webhook-TimestampISO 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 read error defensively.

Error Codes

Status Codeerror.codeCommon Causes
400VALIDATION_ERROR, INVALID_INPUTMissing required fields, invalid JSON, failed schema validation
401UNAUTHORIZEDMissing or expired token, invalid API key
403FORBIDDENInsufficient permissions, or missing the required API-key scope
404NOT_FOUNDResource does not exist or belongs to another company
409CONFLICTConflicting state, such as a scan already in progress
429RATE_LIMIT_EXCEEDEDRate limit exceeded (see Retry-After header)
500INTERNAL_ERROR, DATABASE_ERRORUnexpected server failure — contact support if persistent
503SERVICE_UNAVAILABLEA dependency is temporarily unavailable

Note: Schema validation failures return 400, not 422.

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.

TierApplies toDefault limit
api-keyAny request authenticated with an API key2,000 requests/min
api-readGET requests600 requests/min
api-writePOST, PUT, PATCH requests120 requests/min
api-deleteDELETE requests60 requests/min
authLogin, registration, and password-reset endpoints200 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:

HeaderDescription
X-RateLimit-LimitMaximum requests allowed in the current window
X-RateLimit-RemainingRequests remaining in the current window
X-RateLimit-ResetUnix timestamp when the window resets
X-RateLimit-PolicyWhich 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:

ParameterTypeDefaultDescription
pageinteger1Page number to return
limitinteger50Number 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 page is present, pick one style per integration and stick to it rather than adding page conditionally.


Next Steps

On this page