Skip to content

REST API

Hypertask’s public REST API is available at https://app.hypertask.ai/api/v1. Use it to list projects, create and update tasks, manage comments, assign users, read inbox notifications, and manage task drafts.

Send an API key in the Authorization header:

Terminal window
Authorization: Bearer htk_...

Create API keys in the Hypertask app with Ctrl+K -> REST API. Each user can have up to 10 active keys. The full key is shown once when it is created, and keys can be revoked from the same modal.

Terminal window
export HT_API_KEY=htk_...
curl -s -H "Authorization: Bearer $HT_API_KEY" \
"https://app.hypertask.ai/api/v1/user/context"

Error responses use this general shape:

{
"success": false,
"error": "Validation error",
"message": "project_id must be a positive integer",
"details": {
"field": "project_id",
"code": "invalid_type"
}
}

Previously, several public API routes accepted an unsigned nookies_user cookie as the authenticated user identity. This has been tightened to prevent account takeover via forged cookies:

  • Routes that set nookies_user.id now require a valid, matching signed ht_session cookie with the same id value to succeed.
  • The following route families are affected:
    • Auth routes (e.g., /api/auth/*)
    • MCP routes (/api/mcp/*)
    • Other 64 public sub-paths previously relying on unsigned nookies_user
    • All associated webhook and logged-out call sites

Verified live on production: forging nookies_user={"id":6} now returns 401 instead of 200+ full data, and forging {"id":1} for any other user also returns 401.

Compatibility: The consensus across the auth, MCP, public, webhook, logged-out, and Bearer-auth request families is that this change does not affect existing integrations or tools:

  • Auth requests with standard Authorization: Bearer $HT_API_KEY continue to work.
  • MCP tools retain their same param schema.
  • Public integrations that already manage ht_session via the app workload continue untouched.
  • Work that previously shipped nookies_user directly (e.g., via server-to-server calls) should now explicitly sign or add ht_session with the same id, but it is a rare pattern in practice.

If your pattern relies on unsigned nookies_user outside these call sites, see /api/session/verify to validate the signed session instead.

The createMention API endpoint now requires a verified authentication session. Mentions are no longer an open push channel that trusts caller-supplied identifiers:

  • All requests to POST /api/v1/comments with a mentions array now require verification against the signed ht_session.
  • The mentionedBy field in the request is ignored; it is derived automatically from the authenticated user’s session.
  • The endpoint validates that the task, the initiator, and the recipient all belong to the same board before creating the mention.
  • Attempting to forge mentions or cross-board mentions returns an 401 Unauthorized response.

The following API routes have been secured to require authentication:

  • POST /api/resetUser — admin tool for resetting user state. Takes the same x-admin-password header check as the reset-trial endpoint.
  • GET /api/users/search — searches available users by name or email. Requires a signed ht_session cookie.
  • GET /api/users/getByEmails — returns users matching one or more email addresses. Requires a signed ht_session cookie.

These routes were previously accessible without an authenticated session. Requests made without valid authentication now receive 401 Unauthorized.

The following endpoints have been removed as they were not actively used by any client:

  • GET /api/comments/getAll — use GET /api/v1/comments instead.
  • GET /api/comments/single — use GET /api/v1/comments with a specific ticket_number or task_id.
  • POST /api/notifications/oneOffUpdateScript — use the standard notification management endpoints under /api/v1/notifications/ or the app’s ability to schedule notification preferences.

Knowing these endpoints were already unused, this change does not affect integrations.


Webhooks let external systems subscribe to events on your Hypertask tasks. When an event occurs (such as a task being created, assigned, or updated), Hypertask sends a POST request to your configured webhook URL with the event payload.

Webhook events travel through your subscription’s metadata and are emitted when tasks in that subscription are created, assigned, or updated.

Event nameDescription
task.createdFired when a new task is created.
task.assignedFired when a task is assigned to a user or agent.
task.updatedFired when a task is modified (title, description, priority, sections, etc.).

Each webhook event payload follows this shape:

{
"event": "task.created | task.assigned | task.updated",
"task_id": 789,
"ticket_number": "ENG-42",
"project_id": 123,
"data": {
// Event-specific metadata
},
"timestamp": "2026-08-08T10:30:00Z"
}

Create or update a subscription to define which webhook events to listen to. The subscription’s metadata field is used to signal the relevant task lifecycle events for that subscription through headers such as:

  • X-Event-Type: task.created
  • X-Event-Type: task.assigned
  • X-Event-Type: task.updated

When metadata is set correctly on a subscription, Hypertask emits task lifecycle events to your webhook endpoint for tasks within that subscription’s scope.

To enable task lifecycle events on a subscription, update its metadata to include signal headers:

Terminal window
curl -s -X PATCH \
-H "Authorization: Bearer $HT_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"metadata": {
"X-Event-Type": ["task.created", "task.assigned", "task.updated"]
}
}' \
"https://app.hypertask.ai/api/v1/webhook/subscriptions/SUB_ID"
  • Each event is delivered once per subscription when the corresponding task state changes.
  • Events are queued and sent when subscribed to; updating subscription metadata provides visibility into which events are active.
  • The payload includes explicit task_id, ticket_number, and project_id for routing.

The Hypertask CLI is a thin wrapper around the REST API. Every CLI command ultimately calls a route under https://app.hypertask.ai/api/mcp/*. You can call those same routes directly with curl or any HTTP client, bypassing the Node.js process startup cost.

Measured from a VPS, the difference is substantial:

MethodTypical latency
curl direct to REST API~0.21 s
hypertask CLI~0.84 – 1.06 s

The gap is almost entirely Node.js cold-start time. For agents that issue many sequential calls, this adds up quickly.

How the routes map:

  • The api/v1 routes documented on this page are the public, versioned REST API — use these for integrations and scripts.
  • The api/mcp/* routes are the same operations that the CLI and MCP server use internally. They accept the same authentication header (Authorization: Bearer htk_...).

For agent workloads that currently shell out to the CLI, switching to direct curl calls against either the api/v1 or api/mcp/* routes is a straightforward way to cut per-operation latency by ~4×.

Create or update a saved view through the MCP-compatible REST routes to control the same five-mode subtask setting available from the board’s Ctrl+K menu:

Terminal window
# Create a view that shows subtasks as rows and on parent cards
curl -s -X POST \
-H "Authorization: Bearer $HT_API_KEY" \
-H "Content-Type: application/json" \
-d '{"project_id":15,"title":"All subtasks","subtask_setting":"Flattened_Card"}' \
"https://app.hypertask.ai/api/mcp/view"
# Change only the subtask display mode; other view settings stay unchanged
curl -s -X PATCH \
-H "Authorization: Bearer $HT_API_KEY" \
-H "Content-Type: application/json" \
-d '{"subtask_setting":"Parent"}' \
"https://app.hypertask.ai/api/mcp/view/VIEW_ID"

Accepted values are None, Parent, Flattened, Card, and Flattened_Card. View responses report the effective value as board_subtask_setting.

Routes that operate on a specific task usually accept exactly one task identifier:

Use the internal task ID:

{ "task_id": 789 }

Return the authenticated user, accessible teams, projects, and agents.

Endpoint: GET /api/v1/user/context

Parameters: None

Example request:

Terminal window
curl -s -H "Authorization: Bearer $HT_API_KEY" \
"https://app.hypertask.ai/api/v1/user/context"

Response shape:

{
"success": true,
"user": {
"id": 6,
"email": "valentin@example.com",
"displayName": "Valentin"
},
"connected_agent": null,
"teams": [
{ "id": "team_uuid", "title": "Product" }
],
"projects": [
{ "id": 123, "title": "Engineering", "labels": [], "sections": [] }
],
"all_agents": [
{ "id": "agent_uuid", "displayName": "Release Agent" }
]
}

List projects the API key owner can access.

Endpoint: GET /api/v1/projects

Query parameters:

ParameterTypeRequiredDefaultDescription
statusstringNoNormalFilter by Normal, Archive, or Deleted
searchstringNoSearch project title, name, or description
limitnumberNo50Max results, capped at 100
offsetnumberNo0Pagination offset
sort_bystringNotitletitle, createdAt, or updatedAt
sort_orderstringNoascasc or desc

Example request:

Terminal window
curl -s -H "Authorization: Bearer $HT_API_KEY" \
"https://app.hypertask.ai/api/v1/projects?status=Normal&limit=20"

Response shape:

{
"success": true,
"projects": [
{
"id": 123,
"title": "Engineering",
"description": "Product engineering work",
"memberCount": 5,
"taskCount": 42,
"sections": [
{ "id": 456, "section_title": "Todo" }
],
"labels": [
{ "id": "label_uuid", "name": "Customer" }
],
"status": "Normal",
"createdAt": "2026-03-10T12:00:00.000Z"
}
],
"total": 1,
"limit": 20,
"offset": 0
}

List sections for a project board.

Endpoint: GET /api/v1/projects/{projectId}/sections

Path parameters:

ParameterTypeRequiredDescription
projectIdnumberYesProject ID

Query parameters:

ParameterTypeRequiredDefaultDescription
include_hiddenbooleanNofalseInclude hidden sections

Example request:

Terminal window
curl -s -H "Authorization: Bearer $HT_API_KEY" \
"https://app.hypertask.ai/api/v1/projects/123/sections?include_hidden=false"

Response shape:

{
"success": true,
"sections": [
{
"id": 456,
"section_title": "Todo",
"projectId": 123,
"visibility": true,
"deleted": false,
"ranking": "a0",
"taskCount": 12
}
],
"projectId": 123
}

Create a section in a project.

Endpoint: POST /api/v1/projects/{projectId}/sections

Path parameters:

ParameterTypeRequiredDescription
projectIdnumberYesProject ID

Body parameters:

ParameterTypeRequiredDescription
titlestringYesSection name, 1 to 200 characters
after_section_idnumberNoInsert the new section after this section ID

Example request:

Terminal window
curl -s -X POST -H "Authorization: Bearer $HT_API_KEY" \
-H "Content-Type: application/json" \
-d '{"title":"QA","after_section_id":456}' \
"https://app.hypertask.ai/api/v1/projects/123/sections"

Response shape:

{
"success": true,
"section": {
"id": 457,
"section_title": "QA",
"projectId": 123,
"taskCount": 0
},
"message": "Section created successfully"
}

Rename or reorder a section.

Endpoint: PATCH /api/v1/projects/{projectId}/sections/{sectionId}

Path parameters:

ParameterTypeRequiredDescription
projectIdnumberYesProject ID
sectionIdnumberYesSection ID

Body parameters:

ParameterTypeRequiredDescription
titlestringNoNew section name
move_after_section_idnumberNoMove the section after this section ID

Provide at least one body parameter.

Example request:

Terminal window
curl -s -X PATCH -H "Authorization: Bearer $HT_API_KEY" \
-H "Content-Type: application/json" \
-d '{"title":"Ready for QA"}' \
"https://app.hypertask.ai/api/v1/projects/123/sections/456"

Response shape:

{
"success": true,
"section": {
"id": 456,
"section_title": "Ready for QA",
"projectId": 123,
"taskCount": 4
},
"message": "Section updated successfully"
}

Delete a section and move its tasks to the first remaining section.

Endpoint: DELETE /api/v1/projects/{projectId}/sections/{sectionId}

Path parameters:

ParameterTypeRequiredDescription
projectIdnumberYesProject ID
sectionIdnumberYesSection ID

Example request:

Terminal window
curl -s -X DELETE -H "Authorization: Bearer $HT_API_KEY" \
"https://app.hypertask.ai/api/v1/projects/123/sections/456"

Response shape:

{
"success": true,
"message": "Section deleted successfully"
}

Create a label in a project.

Endpoint: POST /api/v1/projects/{projectId}/labels

Path parameters:

ParameterTypeRequiredDescription
projectIdnumberYesProject ID

Body parameters:

ParameterTypeRequiredDescription
namestringYesLabel name, 1 to 100 characters. Must be unique in the project.

Example request:

Terminal window
curl -s -X POST -H "Authorization: Bearer $HT_API_KEY" \
-H "Content-Type: application/json" \
-d '{"name":"Customer"}' \
"https://app.hypertask.ai/api/v1/projects/123/labels"

Response shape:

{
"success": true,
"label": {
"id": "label_uuid",
"name": "Customer"
},
"message": "Label \"Customer\" created successfully"
}

List project members for mentions and assignment.

Endpoint: GET /api/v1/projects/{projectId}/members

Path parameters:

ParameterTypeRequiredDescription
projectIdnumberYesProject ID

Example request:

Terminal window
curl -s -H "Authorization: Bearer $HT_API_KEY" \
"https://app.hypertask.ai/api/v1/projects/123/members"

Response shape:

{
"success": true,
"members": [
{
"id": 6,
"email": "valentin@example.com",
"displayName": "Valentin"
}
],
"projectId": 123
}

Invite or add a project member by email or user ID.

Endpoint: POST /api/v1/projects/{projectId}/members

Path parameters:

ParameterTypeRequiredDescription
projectIdnumberYesProject ID

Body parameters:

ParameterTypeRequiredDescription
userToAddstring or numberYesEmail address or positive integer user ID

Example request:

Terminal window
curl -s -X POST -H "Authorization: Bearer $HT_API_KEY" \
-H "Content-Type: application/json" \
-d '{"userToAdd":"teammate@example.com"}' \
"https://app.hypertask.ai/api/v1/projects/123/members"

Response shape:

{
"success": true,
"projectId": 123
}

Remove a project member by email or user ID.

Endpoint: DELETE /api/v1/projects/{projectId}/members

Path parameters:

ParameterTypeRequiredDescription
projectIdnumberYesProject ID

Body parameters:

ParameterTypeRequiredDescription
userToRemovestring or numberYesEmail address or positive integer user ID

Example request:

Terminal window
curl -s -X DELETE -H "Authorization: Bearer $HT_API_KEY" \
-H "Content-Type: application/json" \
-d '{"userToRemove":"teammate@example.com"}' \
"https://app.hypertask.ai/api/v1/projects/123/members"

Response shape:

{
"success": true,
"projectId": 123,
"message": "Member removed successfully"
}

Create a board under a team from a structured manifest. Find team IDs with GET /api/v1/user/context.

Endpoint: POST /api/v1/teams/{teamId}/boards

Path parameters:

ParameterTypeRequiredDescription
teamIdstringYesTeam UUID

Headers:

HeaderRequiredDescription
Idempotency-KeyNoReplays with the same key and same body return the cached successful response for 24 hours

Body parameters:

ParameterTypeRequiredDescription
titlestringYesBoard title, 1 to 200 characters
descriptionstringNoBoard description
sectionsobject[]Yes1 to 50 sections. Each item: { "title": string }
labelsobject[]NoUp to 100 labels. Each item: { "name": string, "color"?: string }. color is accepted but not persisted.
tasksobject[]NoUp to 500 starter tasks
tasks[].titlestringYesTask title, 1 to 500 characters
tasks[].descriptionstringNoHTML task description
tasks[].section_indexnumberConditionalZero-based section index. Provide this or section_title, not both.
tasks[].section_titlestringConditionalSection title. Provide this or section_index, not both.
tasks[].label_namesstring[]NoLabel names defined in the same manifest
tasks[].prioritynumberNo0 No Priority, 1 Urgent, 2 High, 3 Medium, 4 Low
tasks[].estimatenumberNo0 none, 2 XS, 3 S, 4 M, 5 L, 6 XL
tasks[].due_datestringNoISO 8601 date or datetime

Example request:

Terminal window
curl -s -X POST -H "Authorization: Bearer $HT_API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: board-import-001" \
-d '{
"title": "Launch",
"sections": [
{ "title": "Todo" },
{ "title": "Done" }
],
"labels": [
{ "name": "Docs" }
],
"tasks": [
{
"title": "Draft launch plan",
"section_index": 0,
"label_names": ["Docs"],
"priority": 2
}
]
}' \
"https://app.hypertask.ai/api/v1/teams/team_uuid/boards"

Response shape:

{
"success": true,
"team_id": "team_uuid",
"board": {
"id": 123,
"title": "Launch"
},
"sections": [
{ "id": 456, "title": "Todo" }
],
"labels": [
{ "id": "label_uuid", "name": "Docs" }
],
"tasks": [
{ "id": 789, "ticketNumber": "ENG-42", "title": "Draft launch plan" }
],
"message": "Board created successfully"
}

Descriptions and comments are stored as HTML, and hand-building that HTML is the single most common thing agents get wrong. Send content_type: "markdown" and the API converts it for you.

It is accepted on three routes:

RouteField converted
POST /api/mcp/commentstext
POST /api/mcp/tasks/createdescription
POST /api/mcp/tasks/updatedescription

content_type is optional and takes "html" or "markdown". Omit it and nothing changes: the field is treated as HTML exactly as before. Any other value returns 400.

Terminal window
curl -X POST https://app.hypertask.ai/api/mcp/comments \
-H "Authorization: Bearer htk_..." \
-H "Content-Type: application/json" \
-d '{
"project_id": 15,
"unique_index": 4467,
"content_type": "markdown",
"text": "Shipped. **Two** things changed:\n\n- the parser\n- the docs"
}'

GitHub Flavored Markdown is supported, so tables, fenced code and task lists all work.

Two conversions are deliberate and worth knowing before you rely on them:

  • Images become links. ![alt](url) renders as an anchor, never an <img>. Inline images crash the editor, so this is not negotiable. An image with no URL falls back to its alt text.
  • Raw HTML in markdown source is escaped, not rendered. Writing <script> in a markdown body produces the visible text <script>, not a live tag. If you want real HTML, send content_type: "html".