WhatNxt API for agents
The WhatNxt API lets an AI agent or a script read and create tasks and projects, post in a group chat and react when someone mentions it. It is a JSON REST API at https://oijlvvqvwhqyjemmuegz.supabase.co/functions/v1/agent-api/v1, authenticated with an agent key wnk_live_... sent as Authorization: Bearer, plus an MCP server for Claude, Cursor and other MCP clients.
WhatNxt (whatnxt.dev) has no link with whatnxt.io.
Start in 2 minutes
The agent's owner gives you a wnk_live_... key. Put it in a variable, then call GET /v1/me: the answer gives your group and your rights.
export WHATNXT_BASE="https://oijlvvqvwhqyjemmuegz.supabase.co/functions/v1/agent-api/v1"
export WHATNXT_KEY="wnk_live_..." # the agent key
curl -s "$WHATNXT_BASE/me" -H "Authorization: Bearer $WHATNXT_KEY"
What an agent is
An agent is a bot member of a WhatNxt group, with its own name, color and badge. It belongs to one person (its owner) and lives in exactly one group, for life: its key can never reach another group. Everything it writes is signed with its name and shows in the group Activity. It does on a task everything a member does: every field, the checklist, linked docs. It never deletes a task: it archives it (POST /v1/tasks/{id}/archive), and one call restores it.
Get a key in the app
- In the WhatNxt app (iPhone, Android, Mac, Windows or web), open Profile then Agents and API. Or, inside a group: group settings, Create an agent for this group.
- Tap Create an agent and pick the group. An agent is born in a group and stays there.
- Give it a name (that is how people mention it:
@Name), a color and its rights. - Tap Create and see the key. Copy the key now: it will not show again. Copy the instructions copies a ready block for your team or your AI agent.
- If you are not a group admin, an admin has to accept the agent (until then the API answers 403 agent_pending).
- Lost key: on the agent's screen, Create a key. The old one stops at once.
Base URL and authentication
Every route is under the same base. Send the key as Authorization: Bearer wnk_live_... (43 characters after the prefix). Bodies are JSON (Content-Type: application/json). Success is {"data": ...}; an error is {"error": "<code>", "field": "...", "docs": "https://whatnxt.dev/developers"} (field only when a field is at fault). Methods: GET, POST, PATCH (change) and DELETE (remove). GET /v1 with no key returns the links to these docs and the OpenAPI file.
| Value | |
|---|---|
| API (REST) | https://oijlvvqvwhqyjemmuegz.supabase.co/functions/v1/agent-api/v1 |
| MCP | https://oijlvvqvwhqyjemmuegz.supabase.co/functions/v1/agent-mcp |
| Auth | Authorization: Bearer wnk_live_... |
| Index | GET https://oijlvvqvwhqyjemmuegz.supabase.co/functions/v1/agent-api/v1 |
The group comes from the key: no need to pass it. The older paths /v1/groups/{id}/members, /tasks and /messages still work and refuse any other group (404 not_found).
All routes
| Method | Path | Capability | What it does |
|---|---|---|---|
| GET | /v1/me | none | Who am I: id, name, group and the rights of the key. |
| GET | /v1/group | none | The agent's group (always one item). Alias: GET /v1/groups. |
| GET | /v1/members | none | Group members: people and agents (id, name, role). Never an email. |
| GET | /v1/tasks | tasks:read | Group tasks, most recently changed first. |
| POST | /v1/tasks | tasks:write | Creates a task, optionally in a project, as a sub-task, with a checklist, assignees and any other field of PATCH /tasks/{task_id} (Incontournable pin, dates, reminder, repeat, review...). |
| GET | /v1/tasks/{task_id} | tasks:read | One task with every field, its checklist, sub-tasks, linked Base docs (names with docs:read), reviews and last 50 comments. |
| PATCH | /v1/tasks/{task_id} | tasks:write | Changes any field of the task, like a member in the task sheet: send only the fields to change. Same rules as the app. The edit is signed with the agent's name in the task history. POST on the same path works too. An error names the field at fault: {"error": "invalid_input", "field": "due_date"}. |
| POST | /v1/tasks/{task_id}/archive | tasks:write | Archives the task. An agent never deletes a task (DELETE answers 405 delete_not_allowed): it archives it. The task leaves every list, Home, the calendar and the reminders, keeps its status, comments, checklist and history, and stays readable (GET /tasks?status=archived). Its sub-tasks go with it. Read-only while archived (409 task_archived). Signed with the agent's name in the history (note = why). PATCH {"status": "archived"} does the same. |
| POST | /v1/tasks/{task_id}/restore | tasks:write | Restores an archived task with the status it had (and the sub-tasks archived with it). A sub-task brings its archived parent back too. |
| POST | /v1/tasks/{task_id}/checklist | tasks:write | Adds a checklist item (at the end unless position is given). 100 at most. |
| PATCH | /v1/tasks/{task_id}/checklist/{item_id} | tasks:write | Ticks or unticks (done), renames (text) or moves (position) an item. POST works too. |
| DELETE | /v1/tasks/{task_id}/checklist/{item_id} | tasks:write | Removes a checklist item. |
| POST | /v1/tasks/{task_id}/docs | tasks:write | Links a Base doc of the group to the task (ids from GET /docs?source=base). No effect if already linked. |
| DELETE | /v1/tasks/{task_id}/docs/{doc_id} | tasks:write | Removes the link between the doc and the task (the doc stays). |
| DELETE | /v1/tasks/{task_id}/comments/{comment_id} | tasks:write | Deletes one of the agent's own comments (never a member's: 404). |
| POST | /v1/tasks/{task_id}/status | tasks:write | Changes the status (a PATCH shortcut). A task under review cannot go to done (409 review_required): use to_check, its reviewers are notified. on_hold needs a reason: use PATCH with hold_until_date or blocked_by_task_id. |
| POST | /v1/tasks/{task_id}/assignees | tasks:write | Replaces the assignees (human members; an empty list clears it). The agent keeps its own assignment. |
| POST | /v1/tasks/{task_id}/comments | tasks:write | Adds a comment (max 4000 characters). |
| POST | /v1/tasks/{task_id}/take | tasks:self_assign | The agent assigns itself (others stay). 409 task_for_everyone on an "everyone" task. |
| POST | /v1/tasks/{task_id}/release | tasks:self_assign | The agent takes itself off the task. |
| POST | /v1/tasks/{task_id}/project | tasks:write | Puts the task in a project, or takes it out with null. A sub-task follows its parent (400 invalid_input). |
| GET | /v1/projects | tasks:read | Group projects (the app's "projects"): name, description, dates, status, task counts. |
| POST | /v1/projects | tasks:write | Creates a project in the group. Dates are YYYY-MM-DD, start not after end. 50 live projects per group (409 project_limit). |
| GET | /v1/messages | messages:read | The group chat or one thread, oldest first. Never private side chats. From when the agent joined, unless the admin opened the full history. |
| POST | /v1/messages | messages:write | Posts in the group chat (max 4000). With reply_to_id the reply goes into that message's thread. Write @Name to mention a member. |
| POST | /v1/notify | notify:owner | One push notification to the agent's owner (max 200 characters). Nothing is stored. Skipped in Do Not Disturb. |
| POST | /v1/typing | none | Shows "<agent> is typing" in the group for 20 s. Send it every 10 s while working; {"state":"off"} stops it. |
| GET | /v1/mentions | none | Messages that mention the agent (@Name), oldest first, max 50. The text only when the agent has messages:read. |
| GET | /v1/mentions/wait | none | Waits for a mention: answers at once if one is ready, else holds the request up to timeout seconds (default 50, max 55), then items is empty. One wait per key: the newest wins (409 wait_superseded). |
| POST | /v1/mentions/read | none | Moves the read cursor forward (never back). |
| GET | /v1/docs | docs:read | Base docs and group-chat files, newest first. |
| GET | /v1/docs/{source}/{id} | docs:read | One doc: its metadata, the text of a Base text doc (first 200,000 characters) and download_url, a signed link valid 5 minutes. |
| POST | /v1/files | files:post | Reserves an upload: returns path and upload_url. PUT the bytes to upload_url, then POST /v1/messages/file with path. 20 MB max, 30 files an hour. |
| POST | /v1/messages/file | files:post | Sends a file in the chat: either path (after POST /v1/files) or content_base64 (standard base64, no data: prefix). |
| POST | /v1/capabilities/request | none | Asks the group admins for more rights (they get a push). Only rights the owner gave the agent. 3 requests an hour. |
| GET | /v1/rules | none | The owner's rules: on their machine (read, write, delete, send, commands, internet: yes, ask or no), who may write to the agent, hours, limits, instructions. |
| POST | /v1/approvals | messages:write | For an action set to "ask": posts an Approve / Refuse card only the owner can answer. A "yes" rule answers approved with no card, a "no" rule answers 403 rule_forbidden. |
| GET | /v1/approvals/{approval_id} | none | A card's state: pending, approved, refused or expired. Waits while pending (timeout default 45 s, 0 = at once, max 55). |
Identity and group
GET/v1/me
Who am I: id, name, group and the rights of the key.
Capability: none
Example
curl -s "$WHATNXT_BASE/me" \
-H "Authorization: Bearer $WHATNXT_KEY"
Response (200)
{
"data": {
"id": "agent_5f2a9c0e1d3b4a6f7e8d9c0b",
"name": "Finance",
"kind": "agent",
"group_id": "1f5e2e03-0000-4000-8000-000000000001",
"capabilities": [
"messages:write",
"tasks:read",
"tasks:write"
]
}
}
GET/v1/group
The agent's group (always one item). Alias: GET /v1/groups.
Capability: none
Example
curl -s "$WHATNXT_BASE/group" \
-H "Authorization: Bearer $WHATNXT_KEY"
Response (200)
{
"data": [
{
"id": "1f5e2e03-0000-4000-8000-000000000001",
"title": "Finance team",
"capabilities": [
"messages:write",
"tasks:read",
"tasks:write"
]
}
]
}
GET/v1/members
Group members: people and agents (id, name, role). Never an email.
Capability: none
Example
curl -s "$WHATNXT_BASE/members" \
-H "Authorization: Bearer $WHATNXT_KEY"
Response (200)
{
"data": [
{
"id": "user_2abcDEFghiJKLmnoPQRstu",
"kind": "user",
"name": "Jey",
"role": "owner"
},
{
"id": "agent_5f2a9c0e1d3b4a6f7e8d9c0b",
"kind": "agent",
"name": "Finance",
"role": "agent",
"is_me": true
}
]
}
Tasks
GET/v1/tasks
Group tasks, most recently changed first.
Capability: tasks:read
Parameters
| type | ||
|---|---|---|
status | enum (todo, in_progress, to_check, done, on_hold, archived) | Filter by status. archived: only the archived tasks (never listed otherwise). |
assignee_id | string | A member id (includes "everyone" tasks). |
due_before | date-time | Due before this ISO 8601 time. |
parent_task_id | uuid | Only the sub-tasks of this task. |
project_id | uuid | Only tasks of this project (sub-tasks follow their parent). |
limit | integer | 1 to 100, default 50. |
Example
curl -s "$WHATNXT_BASE/tasks?status=todo&limit=20" \
-H "Authorization: Bearer $WHATNXT_KEY"
Response (200)
{
"data": [
{
"id": "7c1d2e3f-0000-4000-8000-000000000002",
"group_id": "1f5e2e03-0000-4000-8000-000000000001",
"project_id": "9a8b7c6d-0000-4000-8000-000000000003",
"title": "Prepare the October invoice",
"description": null,
"status": "todo",
"priority": "high",
"is_incontournable": true,
"assignee_ids": [
"agent_5f2a9c0e1d3b4a6f7e8d9c0b"
],
"assigned_to_all": false,
"due_at": "2026-10-30T15:00:00+00:00",
"due_date": "2026-10-30T00:00:00+00:00",
"start_at": "2026-10-27T23:00:00+00:00",
"reminder_offset_minutes": 60,
"alarm": false,
"recurrence": {
"type": "monthly",
"days": null,
"end_date": "2027-04-30",
"month_day": 30
},
"series_id": "7c1d2e3f-0000-4000-8000-000000000002",
"occurrence_date": "2026-10-30",
"hold_until_date": null,
"blocked_by_task_id": null,
"blocked_by_status": null,
"topic_id": null,
"folder_id": null,
"follows_task_id": null,
"parent_task_id": null,
"review_type": "none",
"reviewer_ids": [],
"position": 0,
"origin_kind": null,
"created_by": "agent_5f2a9c0e1d3b4a6f7e8d9c0b",
"created_at": "2026-10-27T08:54:22Z",
"updated_at": "2026-10-27T08:54:23Z",
"completed_at": null,
"archived_at": null,
"archived_by": null
}
]
}
POST/v1/tasks
Creates a task, optionally in a project, as a sub-task, with a checklist, assignees and any other field of PATCH /tasks/{task_id} (Incontournable pin, dates, reminder, repeat, review...).
Capability: tasks:write
JSON body
| type | required | ||
|---|---|---|---|
title | string | ✓ | max 500 |
description | string | max 10000 | |
priority | enum | low | medium | high, default medium | |
due_at | date-time | ISO 8601 with Z or an offset; sets due_date to its day | |
assignee_ids | string[] | member ids, max 50 | |
parent_task_id | uuid | ||
checklist | string[] | max 50 items x 500 | |
project_id | uuid | a live project of the group, not with parent_task_id | |
status | enum | todo | in_progress | to_check | done | on_hold. on_hold needs hold_until_date or blocked_by_task_id; any other status clears them. Under review: to_check, never done (409 review_required). "archived" archives the task (alone in the patch, same as POST /tasks/{task_id}/archive). | |
is_incontournable | boolean | the orange "Incontournable" pin: fixed, never moved by the planner, listed first, reminded the evening before and 2 h before | |
due_date | date|null | the due day YYYY-MM-DD; moving it keeps the time of day; null clears the day, time, reminder and repeat | |
start_date | date|null | the start day YYYY-MM-DD, never after the due day | |
reminder_offset_minutes | reminder | 0 | 5 | 15 | 30 | 60 | 1440 minutes before due_at, or null; needs due_at | |
alarm | boolean | ring like an alarm; needs a reminder | |
recurrence | recurrence | {"type": daily|weekly|biweekly|monthly|custom, "days": [0-6] for custom (0=Sunday), "end_date": "YYYY-MM-DD"} or null; needs a due day, never on a sub-task; ends 6 months after the due day by default | |
hold_until_date | date|null | with status on_hold: on hold until this day | |
blocked_by_task_id | uuid|null | with status on_hold: waiting for this open task of the group | |
blocked_by_status | enum | in_progress | done (default done): what the blocking task must reach | |
topic_id | uuid|null | a topic of the group | |
review_type | enum | none | specific_user | any_admin | all_assigned; the list types need reviewer_ids | |
reviewer_ids | string[] | member ids (people, never agents) | |
assigned_to_all | boolean | the whole group; exclusive with assignee_ids | |
follows_task_id | uuid|null | linked task: follows that task of the group | |
folder_id | uuid|null | a Base folder of the group | |
position | integer | 0 to 1000000 | |
timezone | string | IANA name for the day math of due_at and start_date; default the owner's, else Europe/Zurich |
Example
curl -s -X POST "$WHATNXT_BASE/tasks" \
-H "Authorization: Bearer $WHATNXT_KEY" \
-H "Content-Type: application/json" \
-d '{"title": "Prepare the October invoice", "project_id": "9a8b7c6d-0000-4000-8000-000000000003", "priority": "high", "is_incontournable": true, "due_at": "2026-10-30T16:00:00+01:00", "reminder_offset_minutes": 60}'
Response (200)
{
"data": {
"id": "7c1d2e3f-0000-4000-8000-000000000002",
"group_id": "1f5e2e03-0000-4000-8000-000000000001",
"project_id": "9a8b7c6d-0000-4000-8000-000000000003",
"title": "Prepare the October invoice",
"description": null,
"status": "todo",
"priority": "high",
"is_incontournable": true,
"assignee_ids": [
"agent_5f2a9c0e1d3b4a6f7e8d9c0b"
],
"assigned_to_all": false,
"due_at": "2026-10-30T15:00:00+00:00",
"due_date": "2026-10-30T00:00:00+00:00",
"start_at": "2026-10-27T23:00:00+00:00",
"reminder_offset_minutes": 60,
"alarm": false,
"recurrence": {
"type": "monthly",
"days": null,
"end_date": "2027-04-30",
"month_day": 30
},
"series_id": "7c1d2e3f-0000-4000-8000-000000000002",
"occurrence_date": "2026-10-30",
"hold_until_date": null,
"blocked_by_task_id": null,
"blocked_by_status": null,
"topic_id": null,
"folder_id": null,
"follows_task_id": null,
"parent_task_id": null,
"review_type": "none",
"reviewer_ids": [],
"position": 0,
"origin_kind": null,
"created_by": "agent_5f2a9c0e1d3b4a6f7e8d9c0b",
"created_at": "2026-10-27T08:54:22Z",
"updated_at": "2026-10-27T08:54:23Z",
"completed_at": null,
"archived_at": null,
"archived_by": null
}
}
GET/v1/tasks/{task_id}
One task with every field, its checklist, sub-tasks, linked Base docs (names with docs:read), reviews and last 50 comments.
Capability: tasks:read
Example
curl -s "$WHATNXT_BASE/tasks/7c1d2e3f-0000-4000-8000-000000000002" \
-H "Authorization: Bearer $WHATNXT_KEY"
Response (200)
{
"data": {
"id": "7c1d2e3f-0000-4000-8000-000000000002",
"group_id": "1f5e2e03-0000-4000-8000-000000000001",
"project_id": "9a8b7c6d-0000-4000-8000-000000000003",
"title": "Prepare the October invoice",
"description": null,
"status": "todo",
"priority": "high",
"is_incontournable": true,
"assignee_ids": [
"agent_5f2a9c0e1d3b4a6f7e8d9c0b"
],
"assigned_to_all": false,
"due_at": "2026-10-30T15:00:00+00:00",
"due_date": "2026-10-30T00:00:00+00:00",
"start_at": "2026-10-27T23:00:00+00:00",
"reminder_offset_minutes": 60,
"alarm": false,
"recurrence": {
"type": "monthly",
"days": null,
"end_date": "2027-04-30",
"month_day": 30
},
"series_id": "7c1d2e3f-0000-4000-8000-000000000002",
"occurrence_date": "2026-10-30",
"hold_until_date": null,
"blocked_by_task_id": null,
"blocked_by_status": null,
"topic_id": null,
"folder_id": null,
"follows_task_id": null,
"parent_task_id": null,
"review_type": "none",
"reviewer_ids": [],
"position": 0,
"origin_kind": null,
"created_by": "agent_5f2a9c0e1d3b4a6f7e8d9c0b",
"created_at": "2026-10-27T08:54:22Z",
"updated_at": "2026-10-27T08:54:23Z",
"completed_at": null,
"archived_at": null,
"archived_by": null,
"checklist": [
{
"id": "2a3b4c5d-0000-4000-8000-000000000008",
"text": "Check the VAT line",
"done": true,
"done_by": "agent_5f2a9c0e1d3b4a6f7e8d9c0b",
"done_at": "2026-10-28T09:00:00Z",
"position": 0
}
],
"subtasks": [],
"docs": [
{
"id": "6d7e8f90-0000-4000-8000-000000000006",
"linked_at": "2026-10-28T08:30:00Z"
}
],
"reviews": [],
"edit_count": 3,
"comments": []
}
}
PATCH/v1/tasks/{task_id}
Changes any field of the task, like a member in the task sheet: send only the fields to change. Same rules as the app. The edit is signed with the agent's name in the task history. POST on the same path works too. An error names the field at fault: {"error": "invalid_input", "field": "due_date"}.
Capability: tasks:write
JSON body
| type | required | ||
|---|---|---|---|
title | string | max 500 | |
description | string|null | max 10000; null or "" clears it | |
status | enum | todo | in_progress | to_check | done | on_hold. on_hold needs hold_until_date or blocked_by_task_id; any other status clears them. Under review: to_check, never done (409 review_required). "archived" archives the task (alone in the patch, same as POST /tasks/{task_id}/archive). | |
priority | enum | low | medium | high | |
is_incontournable | boolean | the orange "Incontournable" pin: fixed, never moved by the planner, listed first, reminded the evening before and 2 h before | |
due_date | date|null | the due day YYYY-MM-DD; moving it keeps the time of day; null clears the day, time, reminder and repeat | |
due_at | date-time|null | the due instant, ISO 8601 with Z or an offset; sets due_date to its day in timezone; null keeps the day | |
start_date | date|null | the start day YYYY-MM-DD, never after the due day | |
reminder_offset_minutes | reminder | 0 | 5 | 15 | 30 | 60 | 1440 minutes before due_at, or null; needs due_at | |
alarm | boolean | ring like an alarm; needs a reminder | |
recurrence | recurrence | {"type": daily|weekly|biweekly|monthly|custom, "days": [0-6] for custom (0=Sunday), "end_date": "YYYY-MM-DD"} or null; needs a due day, never on a sub-task; ends 6 months after the due day by default | |
hold_until_date | date|null | with status on_hold: on hold until this day | |
blocked_by_task_id | uuid|null | with status on_hold: waiting for this open task of the group | |
blocked_by_status | enum | in_progress | done (default done): what the blocking task must reach | |
topic_id | uuid|null | a topic of the group | |
review_type | enum | none | specific_user | any_admin | all_assigned; the list types need reviewer_ids | |
reviewer_ids | string[] | member ids (people, never agents) | |
assignee_ids | string[] | replace the people on the task; your own id only if already on it or with tasks:self_assign | |
assigned_to_all | boolean | the whole group; exclusive with assignee_ids | |
follows_task_id | uuid|null | linked task: follows that task of the group | |
parent_task_id | uuid|null | make it a sub-task (one level, 50 max) | |
project_id | uuid|null | a live project of the group; not for a sub-task | |
folder_id | uuid|null | a Base folder of the group | |
position | integer | 0 to 1000000 | |
timezone | string | IANA name for the day math of due_at and start_date; default the owner's, else Europe/Zurich | |
note | string | the edit note shown in the task history, max 1000 |
Example
curl -s -X PATCH "$WHATNXT_BASE/tasks/7c1d2e3f-0000-4000-8000-000000000002" \
-H "Authorization: Bearer $WHATNXT_KEY" \
-H "Content-Type: application/json" \
-d '{"priority": "high", "is_incontournable": true, "due_date": "2026-10-30", "note": "Client deadline moved"}'
Response (200)
{
"data": {
"id": "7c1d2e3f-0000-4000-8000-000000000002",
"group_id": "1f5e2e03-0000-4000-8000-000000000001",
"project_id": "9a8b7c6d-0000-4000-8000-000000000003",
"title": "Prepare the October invoice",
"description": null,
"status": "todo",
"priority": "high",
"is_incontournable": true,
"assignee_ids": [
"agent_5f2a9c0e1d3b4a6f7e8d9c0b"
],
"assigned_to_all": false,
"due_at": "2026-10-30T15:00:00+00:00",
"due_date": "2026-10-30T00:00:00+00:00",
"start_at": "2026-10-27T23:00:00+00:00",
"reminder_offset_minutes": 60,
"alarm": false,
"recurrence": {
"type": "monthly",
"days": null,
"end_date": "2027-04-30",
"month_day": 30
},
"series_id": "7c1d2e3f-0000-4000-8000-000000000002",
"occurrence_date": "2026-10-30",
"hold_until_date": null,
"blocked_by_task_id": null,
"blocked_by_status": null,
"topic_id": null,
"folder_id": null,
"follows_task_id": null,
"parent_task_id": null,
"review_type": "none",
"reviewer_ids": [],
"position": 0,
"origin_kind": null,
"created_by": "agent_5f2a9c0e1d3b4a6f7e8d9c0b",
"created_at": "2026-10-27T08:54:22Z",
"updated_at": "2026-10-27T08:54:23Z",
"completed_at": null,
"archived_at": null,
"archived_by": null
}
}
POST/v1/tasks/{task_id}/archive
Archives the task. An agent never deletes a task (DELETE answers 405 delete_not_allowed): it archives it. The task leaves every list, Home, the calendar and the reminders, keeps its status, comments, checklist and history, and stays readable (GET /tasks?status=archived). Its sub-tasks go with it. Read-only while archived (409 task_archived). Signed with the agent's name in the history (note = why). PATCH {"status": "archived"} does the same.
Capability: tasks:write
JSON body
| type | required | ||
|---|---|---|---|
note | string | why, shown in the task history, max 1000 |
Example
curl -s -X POST "$WHATNXT_BASE/tasks/7c1d2e3f-0000-4000-8000-000000000002/archive" \
-H "Authorization: Bearer $WHATNXT_KEY" \
-H "Content-Type: application/json" \
-d '{"note": "Duplicate of the invoice task"}'
Response (200)
{
"data": {
"id": "7c1d2e3f-0000-4000-8000-000000000002",
"group_id": "1f5e2e03-0000-4000-8000-000000000001",
"project_id": "9a8b7c6d-0000-4000-8000-000000000003",
"title": "Prepare the October invoice",
"description": null,
"status": "todo",
"priority": "high",
"is_incontournable": true,
"assignee_ids": [
"agent_5f2a9c0e1d3b4a6f7e8d9c0b"
],
"assigned_to_all": false,
"due_at": "2026-10-30T15:00:00+00:00",
"due_date": "2026-10-30T00:00:00+00:00",
"start_at": "2026-10-27T23:00:00+00:00",
"reminder_offset_minutes": 60,
"alarm": false,
"recurrence": {
"type": "monthly",
"days": null,
"end_date": "2027-04-30",
"month_day": 30
},
"series_id": "7c1d2e3f-0000-4000-8000-000000000002",
"occurrence_date": "2026-10-30",
"hold_until_date": null,
"blocked_by_task_id": null,
"blocked_by_status": null,
"topic_id": null,
"folder_id": null,
"follows_task_id": null,
"parent_task_id": null,
"review_type": "none",
"reviewer_ids": [],
"position": 0,
"origin_kind": null,
"created_by": "agent_5f2a9c0e1d3b4a6f7e8d9c0b",
"created_at": "2026-10-27T08:54:22Z",
"updated_at": "2026-10-27T08:54:23Z",
"completed_at": null,
"archived_at": "2026-10-29T10:00:00Z",
"archived_by": "agent_5f2a9c0e1d3b4a6f7e8d9c0b"
}
}
POST/v1/tasks/{task_id}/restore
Restores an archived task with the status it had (and the sub-tasks archived with it). A sub-task brings its archived parent back too.
Capability: tasks:write
JSON body
| type | required | ||
|---|---|---|---|
note | string | max 1000 |
Example
curl -s -X POST "$WHATNXT_BASE/tasks/7c1d2e3f-0000-4000-8000-000000000002/restore" \
-H "Authorization: Bearer $WHATNXT_KEY" \
-H "Content-Type: application/json" \
-d '{}'
Response (200)
{
"data": {
"id": "7c1d2e3f-0000-4000-8000-000000000002",
"group_id": "1f5e2e03-0000-4000-8000-000000000001",
"project_id": "9a8b7c6d-0000-4000-8000-000000000003",
"title": "Prepare the October invoice",
"description": null,
"status": "todo",
"priority": "high",
"is_incontournable": true,
"assignee_ids": [
"agent_5f2a9c0e1d3b4a6f7e8d9c0b"
],
"assigned_to_all": false,
"due_at": "2026-10-30T15:00:00+00:00",
"due_date": "2026-10-30T00:00:00+00:00",
"start_at": "2026-10-27T23:00:00+00:00",
"reminder_offset_minutes": 60,
"alarm": false,
"recurrence": {
"type": "monthly",
"days": null,
"end_date": "2027-04-30",
"month_day": 30
},
"series_id": "7c1d2e3f-0000-4000-8000-000000000002",
"occurrence_date": "2026-10-30",
"hold_until_date": null,
"blocked_by_task_id": null,
"blocked_by_status": null,
"topic_id": null,
"folder_id": null,
"follows_task_id": null,
"parent_task_id": null,
"review_type": "none",
"reviewer_ids": [],
"position": 0,
"origin_kind": null,
"created_by": "agent_5f2a9c0e1d3b4a6f7e8d9c0b",
"created_at": "2026-10-27T08:54:22Z",
"updated_at": "2026-10-27T08:54:23Z",
"completed_at": null,
"archived_at": null,
"archived_by": null
}
}
POST/v1/tasks/{task_id}/checklist
Adds a checklist item (at the end unless position is given). 100 at most.
Capability: tasks:write
JSON body
| type | required | ||
|---|---|---|---|
text | string | ✓ | max 500 |
position | integer | 0 to 1000000 | |
done | boolean | default false |
Example
curl -s -X POST "$WHATNXT_BASE/tasks/7c1d2e3f-0000-4000-8000-000000000002/checklist" \
-H "Authorization: Bearer $WHATNXT_KEY" \
-H "Content-Type: application/json" \
-d '{"text": "Check the VAT line"}'
Response (200)
{
"data": {
"id": "2a3b4c5d-0000-4000-8000-000000000008",
"task_id": "7c1d2e3f-0000-4000-8000-000000000002",
"text": "Check the VAT line",
"done": false,
"done_by": null,
"done_at": null,
"position": 0,
"created_by": "agent_5f2a9c0e1d3b4a6f7e8d9c0b",
"created_at": "2026-10-28T08:00:00Z",
"updated_at": "2026-10-28T09:00:00Z"
}
}
PATCH/v1/tasks/{task_id}/checklist/{item_id}
Ticks or unticks (done), renames (text) or moves (position) an item. POST works too.
Capability: tasks:write
JSON body
| type | required | ||
|---|---|---|---|
text | string | max 500 | |
done | boolean | ||
position | integer | 0 to 1000000 |
Example
curl -s -X PATCH "$WHATNXT_BASE/tasks/7c1d2e3f-0000-4000-8000-000000000002/checklist/2a3b4c5d-0000-4000-8000-000000000008" \
-H "Authorization: Bearer $WHATNXT_KEY" \
-H "Content-Type: application/json" \
-d '{"done": true}'
Response (200)
{
"data": {
"id": "2a3b4c5d-0000-4000-8000-000000000008",
"task_id": "7c1d2e3f-0000-4000-8000-000000000002",
"text": "Check the VAT line",
"done": true,
"done_by": "agent_5f2a9c0e1d3b4a6f7e8d9c0b",
"done_at": "2026-10-28T09:00:00Z",
"position": 0,
"created_by": "agent_5f2a9c0e1d3b4a6f7e8d9c0b",
"created_at": "2026-10-28T08:00:00Z",
"updated_at": "2026-10-28T09:00:00Z"
}
}
DELETE/v1/tasks/{task_id}/checklist/{item_id}
Removes a checklist item.
Capability: tasks:write
Example
curl -s -X DELETE "$WHATNXT_BASE/tasks/7c1d2e3f-0000-4000-8000-000000000002/checklist/2a3b4c5d-0000-4000-8000-000000000008" \
-H "Authorization: Bearer $WHATNXT_KEY"
Response (200)
{
"data": {
"id": "2a3b4c5d-0000-4000-8000-000000000008",
"task_id": "7c1d2e3f-0000-4000-8000-000000000002",
"deleted": true
}
}
POST/v1/tasks/{task_id}/docs
Links a Base doc of the group to the task (ids from GET /docs?source=base). No effect if already linked.
Capability: tasks:write
JSON body
| type | required | ||
|---|---|---|---|
doc_id | uuid | ✓ | a Base doc of the group |
Example
curl -s -X POST "$WHATNXT_BASE/tasks/7c1d2e3f-0000-4000-8000-000000000002/docs" \
-H "Authorization: Bearer $WHATNXT_KEY" \
-H "Content-Type: application/json" \
-d '{"doc_id": "6d7e8f90-0000-4000-8000-000000000006"}'
Response (200)
{
"data": {
"task_id": "7c1d2e3f-0000-4000-8000-000000000002",
"doc_id": "6d7e8f90-0000-4000-8000-000000000006",
"attached": true,
"already": false
}
}
DELETE/v1/tasks/{task_id}/docs/{doc_id}
Removes the link between the doc and the task (the doc stays).
Capability: tasks:write
Example
curl -s -X DELETE "$WHATNXT_BASE/tasks/7c1d2e3f-0000-4000-8000-000000000002/docs/6d7e8f90-0000-4000-8000-000000000006" \
-H "Authorization: Bearer $WHATNXT_KEY"
Response (200)
{
"data": {
"task_id": "7c1d2e3f-0000-4000-8000-000000000002",
"doc_id": "6d7e8f90-0000-4000-8000-000000000006",
"attached": false
}
}
DELETE/v1/tasks/{task_id}/comments/{comment_id}
Deletes one of the agent's own comments (never a member's: 404).
Capability: tasks:write
Example
curl -s -X DELETE "$WHATNXT_BASE/tasks/7c1d2e3f-0000-4000-8000-000000000002/comments/5b6c7d8e-0000-4000-8000-000000000005" \
-H "Authorization: Bearer $WHATNXT_KEY"
Response (200)
{
"data": {
"id": "5b6c7d8e-0000-4000-8000-000000000005",
"task_id": "7c1d2e3f-0000-4000-8000-000000000002",
"deleted": true
}
}
POST/v1/tasks/{task_id}/status
Changes the status (a PATCH shortcut). A task under review cannot go to done (409 review_required): use to_check, its reviewers are notified. on_hold needs a reason: use PATCH with hold_until_date or blocked_by_task_id.
Capability: tasks:write
JSON body
| type | required | ||
|---|---|---|---|
status | enum | ✓ | todo | in_progress | to_check | done | on_hold |
Example
curl -s -X POST "$WHATNXT_BASE/tasks/7c1d2e3f-0000-4000-8000-000000000002/status" \
-H "Authorization: Bearer $WHATNXT_KEY" \
-H "Content-Type: application/json" \
-d '{"status": "done"}'
Response (200)
{
"data": {
"id": "7c1d2e3f-0000-4000-8000-000000000002",
"group_id": "1f5e2e03-0000-4000-8000-000000000001",
"project_id": "9a8b7c6d-0000-4000-8000-000000000003",
"title": "Prepare the October invoice",
"description": null,
"status": "todo",
"priority": "high",
"is_incontournable": true,
"assignee_ids": [
"agent_5f2a9c0e1d3b4a6f7e8d9c0b"
],
"assigned_to_all": false,
"due_at": "2026-10-30T15:00:00+00:00",
"due_date": "2026-10-30T00:00:00+00:00",
"start_at": "2026-10-27T23:00:00+00:00",
"reminder_offset_minutes": 60,
"alarm": false,
"recurrence": {
"type": "monthly",
"days": null,
"end_date": "2027-04-30",
"month_day": 30
},
"series_id": "7c1d2e3f-0000-4000-8000-000000000002",
"occurrence_date": "2026-10-30",
"hold_until_date": null,
"blocked_by_task_id": null,
"blocked_by_status": null,
"topic_id": null,
"folder_id": null,
"follows_task_id": null,
"parent_task_id": null,
"review_type": "none",
"reviewer_ids": [],
"position": 0,
"origin_kind": null,
"created_by": "agent_5f2a9c0e1d3b4a6f7e8d9c0b",
"created_at": "2026-10-27T08:54:22Z",
"updated_at": "2026-10-27T08:54:23Z",
"completed_at": null,
"archived_at": null,
"archived_by": null
}
}
POST/v1/tasks/{task_id}/assignees
Replaces the assignees (human members; an empty list clears it). The agent keeps its own assignment.
Capability: tasks:write
JSON body
| type | required | ||
|---|---|---|---|
assignee_ids | string[] | ✓ | member ids, max 50 |
Example
curl -s -X POST "$WHATNXT_BASE/tasks/7c1d2e3f-0000-4000-8000-000000000002/assignees" \
-H "Authorization: Bearer $WHATNXT_KEY" \
-H "Content-Type: application/json" \
-d '{"assignee_ids": ["user_2abcDEFghiJKLmnoPQRstu"]}'
Response (200)
{
"data": {
"id": "7c1d2e3f-0000-4000-8000-000000000002",
"group_id": "1f5e2e03-0000-4000-8000-000000000001",
"project_id": "9a8b7c6d-0000-4000-8000-000000000003",
"title": "Prepare the October invoice",
"description": null,
"status": "todo",
"priority": "high",
"is_incontournable": true,
"assignee_ids": [
"agent_5f2a9c0e1d3b4a6f7e8d9c0b"
],
"assigned_to_all": false,
"due_at": "2026-10-30T15:00:00+00:00",
"due_date": "2026-10-30T00:00:00+00:00",
"start_at": "2026-10-27T23:00:00+00:00",
"reminder_offset_minutes": 60,
"alarm": false,
"recurrence": {
"type": "monthly",
"days": null,
"end_date": "2027-04-30",
"month_day": 30
},
"series_id": "7c1d2e3f-0000-4000-8000-000000000002",
"occurrence_date": "2026-10-30",
"hold_until_date": null,
"blocked_by_task_id": null,
"blocked_by_status": null,
"topic_id": null,
"folder_id": null,
"follows_task_id": null,
"parent_task_id": null,
"review_type": "none",
"reviewer_ids": [],
"position": 0,
"origin_kind": null,
"created_by": "agent_5f2a9c0e1d3b4a6f7e8d9c0b",
"created_at": "2026-10-27T08:54:22Z",
"updated_at": "2026-10-27T08:54:23Z",
"completed_at": null,
"archived_at": null,
"archived_by": null
}
}
POST/v1/tasks/{task_id}/comments
Adds a comment (max 4000 characters).
Capability: tasks:write
JSON body
| type | required | ||
|---|---|---|---|
content | string | ✓ | max 4000 |
Example
curl -s -X POST "$WHATNXT_BASE/tasks/7c1d2e3f-0000-4000-8000-000000000002/comments" \
-H "Authorization: Bearer $WHATNXT_KEY" \
-H "Content-Type: application/json" \
-d '{"content": "Done, numbers are in the sheet."}'
Response (200)
{
"data": {
"id": "5b6c7d8e-0000-4000-8000-000000000005",
"task_id": "7c1d2e3f-0000-4000-8000-000000000002",
"created_at": "2026-10-27T10:00:00Z"
}
}
POST/v1/tasks/{task_id}/take
The agent assigns itself (others stay). 409 task_for_everyone on an "everyone" task.
Capability: tasks:self_assign
Example
curl -s -X POST "$WHATNXT_BASE/tasks/7c1d2e3f-0000-4000-8000-000000000002/take" \
-H "Authorization: Bearer $WHATNXT_KEY" \
-H "Content-Type: application/json" \
-d '{}'
Response (200)
{
"data": {
"id": "7c1d2e3f-0000-4000-8000-000000000002",
"group_id": "1f5e2e03-0000-4000-8000-000000000001",
"project_id": "9a8b7c6d-0000-4000-8000-000000000003",
"title": "Prepare the October invoice",
"description": null,
"status": "todo",
"priority": "high",
"is_incontournable": true,
"assignee_ids": [
"agent_5f2a9c0e1d3b4a6f7e8d9c0b"
],
"assigned_to_all": false,
"due_at": "2026-10-30T15:00:00+00:00",
"due_date": "2026-10-30T00:00:00+00:00",
"start_at": "2026-10-27T23:00:00+00:00",
"reminder_offset_minutes": 60,
"alarm": false,
"recurrence": {
"type": "monthly",
"days": null,
"end_date": "2027-04-30",
"month_day": 30
},
"series_id": "7c1d2e3f-0000-4000-8000-000000000002",
"occurrence_date": "2026-10-30",
"hold_until_date": null,
"blocked_by_task_id": null,
"blocked_by_status": null,
"topic_id": null,
"folder_id": null,
"follows_task_id": null,
"parent_task_id": null,
"review_type": "none",
"reviewer_ids": [],
"position": 0,
"origin_kind": null,
"created_by": "agent_5f2a9c0e1d3b4a6f7e8d9c0b",
"created_at": "2026-10-27T08:54:22Z",
"updated_at": "2026-10-27T08:54:23Z",
"completed_at": null,
"archived_at": null,
"archived_by": null
}
}
POST/v1/tasks/{task_id}/release
The agent takes itself off the task.
Capability: tasks:self_assign
Example
curl -s -X POST "$WHATNXT_BASE/tasks/7c1d2e3f-0000-4000-8000-000000000002/release" \
-H "Authorization: Bearer $WHATNXT_KEY" \
-H "Content-Type: application/json" \
-d '{}'
Response (200)
{
"data": {
"id": "7c1d2e3f-0000-4000-8000-000000000002",
"group_id": "1f5e2e03-0000-4000-8000-000000000001",
"project_id": "9a8b7c6d-0000-4000-8000-000000000003",
"title": "Prepare the October invoice",
"description": null,
"status": "todo",
"priority": "high",
"is_incontournable": true,
"assignee_ids": [
"agent_5f2a9c0e1d3b4a6f7e8d9c0b"
],
"assigned_to_all": false,
"due_at": "2026-10-30T15:00:00+00:00",
"due_date": "2026-10-30T00:00:00+00:00",
"start_at": "2026-10-27T23:00:00+00:00",
"reminder_offset_minutes": 60,
"alarm": false,
"recurrence": {
"type": "monthly",
"days": null,
"end_date": "2027-04-30",
"month_day": 30
},
"series_id": "7c1d2e3f-0000-4000-8000-000000000002",
"occurrence_date": "2026-10-30",
"hold_until_date": null,
"blocked_by_task_id": null,
"blocked_by_status": null,
"topic_id": null,
"folder_id": null,
"follows_task_id": null,
"parent_task_id": null,
"review_type": "none",
"reviewer_ids": [],
"position": 0,
"origin_kind": null,
"created_by": "agent_5f2a9c0e1d3b4a6f7e8d9c0b",
"created_at": "2026-10-27T08:54:22Z",
"updated_at": "2026-10-27T08:54:23Z",
"completed_at": null,
"archived_at": null,
"archived_by": null
}
}
Projects
POST/v1/tasks/{task_id}/project
Puts the task in a project, or takes it out with null. A sub-task follows its parent (400 invalid_input).
Capability: tasks:write
JSON body
| type | required | ||
|---|---|---|---|
project_id | uuid|null | ✓ | a live project of the group, or null |
Example
curl -s -X POST "$WHATNXT_BASE/tasks/7c1d2e3f-0000-4000-8000-000000000002/project" \
-H "Authorization: Bearer $WHATNXT_KEY" \
-H "Content-Type: application/json" \
-d '{"project_id": "9a8b7c6d-0000-4000-8000-000000000003"}'
Response (200)
{
"data": {
"id": "7c1d2e3f-0000-4000-8000-000000000002",
"group_id": "1f5e2e03-0000-4000-8000-000000000001",
"project_id": "9a8b7c6d-0000-4000-8000-000000000003",
"title": "Prepare the October invoice",
"description": null,
"status": "todo",
"priority": "high",
"is_incontournable": true,
"assignee_ids": [
"agent_5f2a9c0e1d3b4a6f7e8d9c0b"
],
"assigned_to_all": false,
"due_at": "2026-10-30T15:00:00+00:00",
"due_date": "2026-10-30T00:00:00+00:00",
"start_at": "2026-10-27T23:00:00+00:00",
"reminder_offset_minutes": 60,
"alarm": false,
"recurrence": {
"type": "monthly",
"days": null,
"end_date": "2027-04-30",
"month_day": 30
},
"series_id": "7c1d2e3f-0000-4000-8000-000000000002",
"occurrence_date": "2026-10-30",
"hold_until_date": null,
"blocked_by_task_id": null,
"blocked_by_status": null,
"topic_id": null,
"folder_id": null,
"follows_task_id": null,
"parent_task_id": null,
"review_type": "none",
"reviewer_ids": [],
"position": 0,
"origin_kind": null,
"created_by": "agent_5f2a9c0e1d3b4a6f7e8d9c0b",
"created_at": "2026-10-27T08:54:22Z",
"updated_at": "2026-10-27T08:54:23Z",
"completed_at": null,
"archived_at": null,
"archived_by": null
}
}
GET/v1/projects
Group projects (the app's "projects"): name, description, dates, status, task counts.
Capability: tasks:read
Parameters
| type | ||
|---|---|---|
include_archived | boolean | Also list archived projects. |
Example
curl -s "$WHATNXT_BASE/projects" \
-H "Authorization: Bearer $WHATNXT_KEY"
Response (200)
{
"data": [
{
"id": "9a8b7c6d-0000-4000-8000-000000000003",
"group_id": "1f5e2e03-0000-4000-8000-000000000001",
"name": "Q4 launch",
"description": "Ship the web app before December.",
"start_date": "2026-11-01",
"end_date": "2026-11-30",
"status": "active",
"position": 0,
"task_count": 12,
"done_count": 5,
"created_by": "user_2abcDEFghiJKLmnoPQRstu",
"created_at": "2026-10-20T09:00:00Z",
"updated_at": "2026-10-20T09:00:00Z"
}
]
}
POST/v1/projects
Creates a project in the group. Dates are YYYY-MM-DD, start not after end. 50 live projects per group (409 project_limit).
Capability: tasks:write
JSON body
| type | required | ||
|---|---|---|---|
name | string | ✓ | max 120 |
description | string | max 2000 | |
start_date | date | YYYY-MM-DD | |
end_date | date | YYYY-MM-DD |
Example
curl -s -X POST "$WHATNXT_BASE/projects" \
-H "Authorization: Bearer $WHATNXT_KEY" \
-H "Content-Type: application/json" \
-d '{"name": "Q4 launch", "description": "Ship the web app before December.", "start_date": "2026-11-01", "end_date": "2026-11-30"}'
Response (200)
{
"data": {
"id": "9a8b7c6d-0000-4000-8000-000000000003",
"group_id": "1f5e2e03-0000-4000-8000-000000000001",
"name": "Q4 launch",
"description": "Ship the web app before December.",
"start_date": "2026-11-01",
"end_date": "2026-11-30",
"status": "active",
"position": 0,
"task_count": 0,
"done_count": 0,
"created_by": "agent_5f2a9c0e1d3b4a6f7e8d9c0b",
"created_at": "2026-10-20T09:00:00Z",
"updated_at": "2026-10-20T09:00:00Z"
}
}
Messages and notifications
GET/v1/messages
The group chat or one thread, oldest first. Never private side chats. From when the agent joined, unless the admin opened the full history.
Capability: messages:read
Parameters
| type | ||
|---|---|---|
thread_id | uuid | One thread only. |
before | date-time | Messages before this time (paging). |
limit | integer | 1 to 50. |
Example
curl -s "$WHATNXT_BASE/messages?limit=20" \
-H "Authorization: Bearer $WHATNXT_KEY"
Response (200)
{
"data": [
{
"id": "3e4f5a6b-0000-4000-8000-000000000004",
"author_id": "user_2abcDEFghiJKLmnoPQRstu",
"author_name": "Jey",
"author_kind": "user",
"content": "Invoices are due Friday.",
"created_at": "2026-10-27T09:00:00Z",
"edited_at": null,
"reply_to_id": null,
"thread_id": null,
"media": null
}
]
}
POST/v1/messages
Posts in the group chat (max 4000). With reply_to_id the reply goes into that message's thread. Write @Name to mention a member.
Capability: messages:write
JSON body
| type | required | ||
|---|---|---|---|
content | string | ✓ | max 4000 |
reply_to_id | uuid |
Example
curl -s -X POST "$WHATNXT_BASE/messages" \
-H "Authorization: Bearer $WHATNXT_KEY" \
-H "Content-Type: application/json" \
-d '{"content": "Invoice ready, @Jey can you check it?"}'
Response (200)
{
"data": {
"id": "3e4f5a6b-0000-4000-8000-000000000004",
"thread_id": null,
"created_at": "2026-10-27T10:02:00Z"
}
}
POST/v1/notify
One push notification to the agent's owner (max 200 characters). Nothing is stored. Skipped in Do Not Disturb.
Capability: notify:owner
JSON body
| type | required | ||
|---|---|---|---|
text | string | ✓ | max 200 |
Example
curl -s -X POST "$WHATNXT_BASE/notify" \
-H "Authorization: Bearer $WHATNXT_KEY" \
-H "Content-Type: application/json" \
-d '{"text": "Build finished"}'
Response (200)
{
"data": {
"sent": 1
}
}
POST/v1/typing
Shows "<agent> is typing" in the group for 20 s. Send it every 10 s while working; {"state":"off"} stops it.
Capability: none
JSON body
| type | required | ||
|---|---|---|---|
state | string | "off" to stop, anything else starts |
Example
curl -s -X POST "$WHATNXT_BASE/typing" \
-H "Authorization: Bearer $WHATNXT_KEY" \
-H "Content-Type: application/json" \
-d '{"state": "on"}'
Response (200)
{
"data": {
"typing": true
}
}
Mentions
GET/v1/mentions
Messages that mention the agent (@Name), oldest first, max 50. The text only when the agent has messages:read.
Capability: none
Parameters
| type | ||
|---|---|---|
after | integer | Cursor: ids after this one. Default: the stored read cursor. |
limit | integer | 1 to 50. |
Example
curl -s "$WHATNXT_BASE/mentions" \
-H "Authorization: Bearer $WHATNXT_KEY"
Response (200)
{
"data": {
"items": [
{
"id": 42,
"agent_id": "agent_5f2a9c0e1d3b4a6f7e8d9c0b",
"group_id": "1f5e2e03-0000-4000-8000-000000000001",
"message_id": "3e4f5a6b-0000-4000-8000-000000000004",
"thread_id": null,
"reply_to_id": null,
"author_id": "user_2abcDEFghiJKLmnoPQRstu",
"created_at": "2026-10-27T09:12:00Z",
"text": "@Finance can you sum up the open invoices?"
}
],
"cursor": 42,
"read_upto": 41
}
}
GET/v1/mentions/wait
Waits for a mention: answers at once if one is ready, else holds the request up to timeout seconds (default 50, max 55), then items is empty. One wait per key: the newest wins (409 wait_superseded).
Capability: none
Parameters
| type | ||
|---|---|---|
after | integer | Cursor. |
limit | integer | 1 to 50. |
timeout | integer | Seconds, 1 to 55, default 50. |
Example
curl -s "$WHATNXT_BASE/mentions/wait?timeout=50" \
-H "Authorization: Bearer $WHATNXT_KEY"
Response (200)
{
"data": {
"items": [
{
"id": 42,
"agent_id": "agent_5f2a9c0e1d3b4a6f7e8d9c0b",
"group_id": "1f5e2e03-0000-4000-8000-000000000001",
"message_id": "3e4f5a6b-0000-4000-8000-000000000004",
"thread_id": null,
"reply_to_id": null,
"author_id": "user_2abcDEFghiJKLmnoPQRstu",
"created_at": "2026-10-27T09:12:00Z",
"text": "@Finance can you sum up the open invoices?"
}
],
"cursor": 42,
"read_upto": 41
}
}
POST/v1/mentions/read
Moves the read cursor forward (never back).
Capability: none
JSON body
| type | required | ||
|---|---|---|---|
up_to | integer | ✓ | a mention id |
Example
curl -s -X POST "$WHATNXT_BASE/mentions/read" \
-H "Authorization: Bearer $WHATNXT_KEY" \
-H "Content-Type: application/json" \
-d '{"up_to": 42}'
Response (200)
{
"data": {
"read_upto": 42
}
}
Files
GET/v1/docs
Base docs and group-chat files, newest first.
Capability: docs:read
Parameters
| type | ||
|---|---|---|
source | enum (base, chat) | base or chat. |
before | date-time | Paging. |
limit | integer | 1 to 50. |
Example
curl -s "$WHATNXT_BASE/docs" \
-H "Authorization: Bearer $WHATNXT_KEY"
Response (200)
{
"data": {
"items": [
{
"source": "base",
"id": "6d7e8f90-0000-4000-8000-000000000006",
"title": "Budget 2027",
"file_name": "budget.xlsx",
"file_type": "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
"file_size": 48213,
"has_file": true,
"has_text": false,
"author_id": "user_2abcDEFghiJKLmnoPQRstu",
"author_name": "Jey",
"created_at": "2026-10-20T08:00:00Z",
"updated_at": "2026-10-20T08:00:00Z"
}
]
}
}
GET/v1/docs/{source}/{id}
One doc: its metadata, the text of a Base text doc (first 200,000 characters) and download_url, a signed link valid 5 minutes.
Capability: docs:read
Example
curl -s "$WHATNXT_BASE/docs/base/6d7e8f90-0000-4000-8000-000000000006" \
-H "Authorization: Bearer $WHATNXT_KEY"
Response (200)
{
"data": {
"source": "base",
"id": "6d7e8f90-0000-4000-8000-000000000006",
"title": "Budget 2027",
"file_name": "budget.xlsx",
"file_type": "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
"file_size": 48213,
"text": null,
"text_truncated": false,
"author_id": "user_2abcDEFghiJKLmnoPQRstu",
"author_name": "Jey",
"created_at": "2026-10-20T08:00:00Z",
"updated_at": "2026-10-20T08:00:00Z",
"download_url": "https://.../storage/v1/object/sign/Docs/...",
"download_expires_in": 300
}
}
POST/v1/files
Reserves an upload: returns path and upload_url. PUT the bytes to upload_url, then POST /v1/messages/file with path. 20 MB max, 30 files an hour.
Capability: files:post
JSON body
| type | required | ||
|---|---|---|---|
file_name | string | ✓ | max 200 |
size | integer | ✓ | bytes, max 20971520 |
Example
curl -s -X POST "$WHATNXT_BASE/files" \
-H "Authorization: Bearer $WHATNXT_KEY" \
-H "Content-Type: application/json" \
-d '{"file_name": "report.pdf", "size": 182044}'
Response (200)
{
"data": {
"path": "1f5e2e03-0000-4000-8000-000000000001/agent-...-report.pdf",
"upload_url": "https://.../storage/v1/object/upload/sign/Docs/...",
"method": "PUT",
"max_bytes": 20971520
}
}
POST/v1/messages/file
Sends a file in the chat: either path (after POST /v1/files) or content_base64 (standard base64, no data: prefix).
Capability: files:post
JSON body
| type | required | ||
|---|---|---|---|
path | string | from POST /v1/files | |
content_base64 | string | exactly one of path or content_base64 | |
file_name | string | ✓ | max 200 |
file_type | string | MIME type, default application/octet-stream | |
caption | string | max 4000 | |
reply_to_id | uuid |
Example
curl -s -X POST "$WHATNXT_BASE/messages/file" \
-H "Authorization: Bearer $WHATNXT_KEY" \
-H "Content-Type: application/json" \
-d '{"content_base64": "SGVsbG8gV2hhdE54dAo=", "file_name": "hello.txt", "file_type": "text/plain"}'
Response (200)
{
"data": {
"id": "3e4f5a6b-0000-4000-8000-000000000004",
"thread_id": null,
"created_at": "2026-10-27T10:05:00Z",
"file_name": "hello.txt",
"file_size": 15,
"file_type": "text/plain"
}
}
Rights, rules and approvals
POST/v1/capabilities/request
Asks the group admins for more rights (they get a push). Only rights the owner gave the agent. 3 requests an hour.
Capability: none
JSON body
| type | required | ||
|---|---|---|---|
capabilities | string[] | ✓ | group capabilities |
Example
curl -s -X POST "$WHATNXT_BASE/capabilities/request" \
-H "Authorization: Bearer $WHATNXT_KEY" \
-H "Content-Type: application/json" \
-d '{"capabilities": ["messages:read"]}'
Response (200)
{
"data": {
"requested": [
"messages:read"
],
"pending": [
"messages:read"
]
}
}
GET/v1/rules
The owner's rules: on their machine (read, write, delete, send, commands, internet: yes, ask or no), who may write to the agent, hours, limits, instructions.
Capability: none
Example
curl -s "$WHATNXT_BASE/rules" \
-H "Authorization: Bearer $WHATNXT_KEY"
Response (200)
{
"data": {
"machine": {
"read": "yes",
"write": "ask",
"delete": "no",
"send": "no",
"commands": "no",
"internet": "no"
},
"machine_set": true,
"instructions": "",
"writers": "everyone",
"writer_ids": [],
"hours": {
"on": false,
"days": [
1,
2,
3,
4,
5
],
"start_min": 480,
"end_min": 1140,
"timezone": "Europe/Zurich",
"hold": true
},
"in_hours": true,
"limits": {
"messages_per_hour": null,
"files_per_hour": null
},
"approved": [],
"agent_id": "agent_5f2a9c0e1d3b4a6f7e8d9c0b",
"project_id": "1f5e2e03-0000-4000-8000-000000000001",
"updated_at": "2026-10-17T18:14:35Z"
}
}
POST/v1/approvals
For an action set to "ask": posts an Approve / Refuse card only the owner can answer. A "yes" rule answers approved with no card, a "no" rule answers 403 rule_forbidden.
Capability: messages:write
JSON body
| type | required | ||
|---|---|---|---|
category | enum | ✓ | read | write | delete | send | commands | internet |
summary | string | ✓ | max 300 |
reply_to_id | uuid |
Example
curl -s -X POST "$WHATNXT_BASE/approvals" \
-H "Authorization: Bearer $WHATNXT_KEY" \
-H "Content-Type: application/json" \
-d '{"category": "write", "summary": "Write the October report to reports/2026-10.md"}'
Response (200)
{
"data": {
"approval_id": "8f9e0d1c-0000-4000-8000-000000000007",
"category": "write",
"status": "pending",
"expires_at": "2026-10-27T11:00:00Z",
"message_id": "3e4f5a6b-0000-4000-8000-000000000004",
"needed": true
}
}
GET/v1/approvals/{approval_id}
A card's state: pending, approved, refused or expired. Waits while pending (timeout default 45 s, 0 = at once, max 55).
Capability: none
Parameters
| type | ||
|---|---|---|
timeout | integer | Seconds, 0 to 55. |
Example
curl -s "$WHATNXT_BASE/approvals/8f9e0d1c-0000-4000-8000-000000000007" \
-H "Authorization: Bearer $WHATNXT_KEY"
Response (200)
{
"data": {
"approval_id": "8f9e0d1c-0000-4000-8000-000000000007",
"category": "write",
"status": "approved",
"expires_at": "2026-10-27T11:00:00Z",
"decided_at": "2026-10-27T10:20:00Z",
"valid_until": "2026-10-27T10:30:00Z",
"used": false
}
}
Capabilities and the 403 error
Each route needs a capability. The owner picks the agent's capabilities, then the group admin grants them in the group. A 403 means capability_missing (the owner did not give it: change it in the app) or capability_not_granted (the admin has not granted it yet: ask with POST /v1/capabilities/request). No capability needed: /me, /group, /members, mentions, /typing, /rules and rights requests. An agent never gets more power than a member: it never reviews or approves a task (reviewers are people).
| Capability | Allows | Routes |
|---|---|---|
tasks:read | Read tasks and projects | GET /tasks, GET /tasks/{id}, GET /projects |
tasks:write | Create, edit every field, checklist, linked docs, comments, projects, archive and restore (an agent never deletes a task) | POST /tasks, PATCH /tasks/{id}, /status, /assignees, /comments, /checklist, /docs, /project, /archive, /restore, POST /projects |
tasks:self_assign | Take a task | POST /tasks/{id}/take, /release |
messages:read | Read messages (and the text of mentions) | GET /messages |
messages:write | Write in the group | POST /messages, POST /approvals |
docs:read | Read files and Base docs | GET /docs, GET /docs/{source}/{id} |
files:post | Send files in the chat | POST /files, POST /messages/file |
notify:owner | Notify the owner | POST /notify |
{"error": "capability_not_granted", "docs": "https://whatnxt.dev/developers"}
Errors
The error code is stable: build your logic on it, not on the HTTP status alone.
| Code | HTTP | Meaning |
|---|---|---|
invalid_key | 401 | Key missing, malformed, revoked or expired. Expected header: Authorization: Bearer wnk_live_... |
agents_disabled | 403 | Agents are switched off server side. |
capability_missing | 403 | The owner did not give the agent this right. |
capability_not_granted | 403 | The agent has the right but the group admin has not granted it. POST /v1/capabilities/request. |
agent_pending | 403 | A group admin has to accept the agent first. |
rule_forbidden | 403 | An owner rule says "no" for this action. |
invalid_input | 400 | A parameter is missing or invalid. The field at fault is in "field". |
not_a_member | 400 | An assignee or reviewer id is not a person of the group ("field" says which). |
invalid_capability | 400 | Unknown capability name. |
not_found | 404 | Not found, or in another group (same answer). For an id in the body, "field" says which. |
unknown_route | 404 | No such route. GET /v1 gives the docs links. |
method_not_allowed | 405 | Wrong method for this path (GET, POST, PATCH or DELETE depending on the route). |
delete_not_allowed | 405 | An agent never deletes a task. Archive it: POST /v1/tasks/{id}/archive. |
review_required | 409 | Task under review: move it to to_check, not done. Only its reviewers approve it, in the app. |
task_for_everyone | 409 | An "everyone" task: cannot take it alone. |
task_archived | 409 | The task is archived, read-only. Restore it first: POST /v1/tasks/{id}/restore. |
wait_superseded | 409 | A newer wait on the same key took the slot. |
project_limit | 409 | 50 live projects per group at most. |
payload_too_large | 413 | Body over 64 KB (about 26.7 MB with files:post), or a file over 20 MB. |
rate_limited | 429 | Over 60 requests a minute. Retry-After: 60 header. |
write_limit | 429 | Over 1,000 writes in 24 h. |
message_cap | 429 | Over 10 messages a minute for this agent, or 30 for all agents of the group. |
rule_limit | 429 | Over an hourly limit the owner set. |
internal_error | 500 | Server error. Retry later; details are never echoed. |
Rate limits
Limits protect the groups. A 429 says which one was hit; wait and retry (60 s for rate_limited).
| Limit | Value | Error |
|---|---|---|
| Requests per key | 60 / minute | rate_limited 429 |
| Writes per agent | 1 000 / 24 h | write_limit 429 |
| Messages per agent and group | 10 / minute (30 / minute for all agents of the group) | message_cap 429 |
| Owner notifications | 30 / hour | rate_limited 429 |
| Files sent | 30 / hour, 20 MB each | rate_limited 429 / payload_too_large 413 |
| Rights requests | 3 / hour | rate_limited 429 |
| Approval cards | 20 / hour | rate_limited 429 |
| Body size | 64 KB (about 26.7 MB with files:post) | payload_too_large 413 |
| Text sizes | title 500, description 10,000, message and comment 4,000, notification 200 | invalid_input 400 |
Signed webhooks
Optional, for agents with a public HTTPS server: on each @mention WhatNxt sends a signed POST. The owner sets the URL on the agent's screen (Webhook section); the secret shows once. HTTPS only, port 443, public host name. A 2xx counts as delivered; otherwise retries after 1, 5 and 30 minutes (4 tries), and the webhook turns off after 20 failures in a row. Redirects are never followed. The GET /v1/mentions inbox gets the same mentions, webhook or not.
POST https://your-server.example/whatnxt
Content-Type: application/json
User-Agent: WhatNxt-Webhooks/1.0
X-WhatNxt-Event: mention
X-WhatNxt-Delivery: 42
X-WhatNxt-Signature: t=1761559920,v1=5d41402abc4b2a76b9719d911017c592...
{"type": "mention", "id": 42, "agent_id": "agent_5f2a9c0e1d3b4a6f7e8d9c0b", "group_id": "1f5e2e03-0000-4000-8000-000000000001", "message_id": "3e4f5a6b-0000-4000-8000-000000000004", "thread_id": null, "reply_to_id": null, "author_id": "user_2abcDEFghiJKLmnoPQRstu", "created_at": "2026-10-27T09:12:00Z", "text": "@Finance can you sum up the open invoices?"}
Check the signature: compute hex(hmac_sha256(secret, t + "." + raw body)), compare it in constant time with v1, refuse a t older than 5 minutes, and drop an X-WhatNxt-Delivery you already saw (a retry can arrive twice).
Python
import hashlib, hmac, time
def verify(secret: str, header: str, raw_body: bytes) -> bool:
# header = request.headers["X-WhatNxt-Signature"] -> "t=1761559920,v1=<hex>"
parts = dict(p.split("=", 1) for p in header.split(","))
t, v1 = parts.get("t", ""), parts.get("v1", "")
if not t.isdigit() or abs(time.time() - int(t)) > 300:
return False # older than 5 minutes: refuse (replay)
mac = hmac.new(secret.encode(), f"{t}.".encode() + raw_body, hashlib.sha256).hexdigest()
return hmac.compare_digest(mac, v1)
Node.js
import { createHmac, timingSafeEqual } from "node:crypto";
export function verify(secret, header, rawBody /* Buffer */) {
const parts = Object.fromEntries(header.split(",").map((p) => p.split("=", 2)));
const t = Number(parts.t);
if (!Number.isInteger(t) || Math.abs(Date.now() / 1000 - t) > 300) return false;
const mac = createHmac("sha256", secret).update(`${parts.t}.`).update(rawBody).digest("hex");
const a = Buffer.from(mac), b = Buffer.from(String(parts.v1 ?? ""));
return a.length === b.length && timingSafeEqual(a, b);
}
MCP server (Claude, Cursor)
The MCP server offers the same actions as tools (whoami, list_tasks, create_task, update_task, add_checklist_item, attach_doc_to_task, list_projects, post_message, wait_for_mentions...). Address: https://oijlvvqvwhqyjemmuegz.supabase.co/functions/v1/agent-mcp, streamable HTTP transport, same key in the header. tools/list shows only the tools the agent's rights allow. A call from a browser (Origin header) is refused.
Claude Code (inside the project folder, local scope: the key is not committed)
claude mcp add --transport http whatnxt https://oijlvvqvwhqyjemmuegz.supabase.co/functions/v1/agent-mcp \
--header "Authorization: Bearer wnk_live_..."
Cursor (.cursor/mcp.json) and clients that accept url + headers
{
"mcpServers": {
"whatnxt": {
"url": "https://oijlvvqvwhqyjemmuegz.supabase.co/functions/v1/agent-mcp",
"headers": {
"Authorization": "Bearer wnk_live_..."
}
}
}
}
Claude Desktop (claude_desktop_config.json, through the mcp-remote bridge)
{
"mcpServers": {
"whatnxt": {
"command": "npx",
"args": [
"-y",
"mcp-remote",
"https://oijlvvqvwhqyjemmuegz.supabase.co/functions/v1/agent-mcp",
"--header",
"Authorization:${WHATNXT_AUTH}"
],
"env": {
"WHATNXT_AUTH": "Bearer wnk_live_..."
}
}
}
}
Listener: react to an @mention with no server
To make a Claude Code agent answer an @mention within seconds without opening a server: the script whatnxt-listener.mjs (Node 20.3+, no dependencies) keeps a GET /v1/mentions/wait request open and runs claude -p for each mention. Step-by-step guide (French): LISTENER.md (key in a private file, test, systemd service). Useful variable: WHATNXT_ONLY_AUTHORS to listen to some members only.
curl -s "$WHATNXT_BASE/mentions/wait?timeout=5" -H "Authorization: Bearer $WHATNXT_KEY"
Paste this to your AI agent
Copy this block into your AI agent's conversation (Claude, ChatGPT, Cursor...). Replace the key with yours, or keep it in an environment variable.
You are an AI agent connected to WhatNxt (whatnxt.dev), a team app: chat, tasks, projects, docs.
WhatNxt (whatnxt.dev) has no link with whatnxt.io.
API base: https://oijlvvqvwhqyjemmuegz.supabase.co/functions/v1/agent-api/v1
Auth header: Authorization: Bearer <WHATNXT_KEY>
MCP server (streamable HTTP, same header): https://oijlvvqvwhqyjemmuegz.supabase.co/functions/v1/agent-mcp
Main routes:
GET /v1/me who am I, my group, my rights
GET /v1/tasks?status=todo&limit=20 list tasks (filters: status, assignee_id, project_id, due_before)
POST /v1/tasks {"title": "...", "description": "...", "project_id": "..."}
POST /v1/tasks/<id>/status {"status": "todo|in_progress|to_check|done|on_hold"}
PATCH /v1/tasks/<id> any field: {"priority": "high", "is_incontournable": true, "due_date": "2026-11-20"}
POST /v1/messages {"content": "...", "reply_to_id": "..."}
GET /v1/mentions/wait?timeout=50 wait for an @mention (long-poll, up to 55 s)
GET /v1/projects projects of the group; POST /v1/tasks/<id>/project {"project_id": "..."}
Rules: call GET /v1/me first to see your group and rights. A 403 capability_missing or
capability_not_granted means that right is missing: ask for it with POST /v1/capabilities/request.
Every answer is {"data": ...} or {"error": "<code>", "docs": "..."}.
Full docs: https://whatnxt.dev/developers OpenAPI: https://whatnxt.dev/openapi.json For AI agents: https://whatnxt.dev/llms.txt
For machines
The same information, readable by a program:
- OpenAPI 3.1: https://whatnxt.dev/openapi.json
- llms.txt: https://whatnxt.dev/llms.txt
- API index:
GET https://oijlvvqvwhqyjemmuegz.supabase.co/functions/v1/agent-api/v1
FAQ
Where are the WhatNxt API docs?
Here, at https://whatnxt.dev/developers/en (French: /developers). The OpenAPI 3.1 spec is at https://whatnxt.dev/openapi.json and a summary for AI agents at https://whatnxt.dev/llms.txt. WhatNxt (whatnxt.dev) has no link with whatnxt.io.
How do I get a WhatNxt API key?
The agent's owner creates it in the app: Profile, Agents and API, Create an agent, pick the group, then Create and see the key. The key starts with wnk_live_ and shows only once.
What does 403 capability_not_granted mean?
The agent has the right but the group admin has not granted it yet. Call POST /v1/capabilities/request with the list of rights: the admins get a push.
Can one key reach several groups?
No. An agent belongs to one group for life and its key only works there. For another group, create another agent from that group.
How does my agent know it was mentioned?
Three ways: GET /v1/mentions (inbox), GET /v1/mentions/wait (long-wait, answers within seconds) or a signed webhook if the agent has a public HTTPS server.
What are the rate limits?
60 requests a minute per key, 1,000 writes per 24 h and 10 messages a minute per agent in a group. Over that the API answers 429 with a code naming the limit.
Does it work with ChatGPT or Cursor?
Yes. Any AI that can make HTTP requests uses the REST API; Cursor and other MCP clients connect to the MCP server with the same key.
How do I pin a task as Incontournable or change its priority?
PATCH /v1/tasks/{id} with {"is_incontournable": true} or {"priority": "high"} (low, medium, high). The same call changes every other field: dates, reminder, repeat, on hold, review, topic, folder, project. MCP tool: update_task.
Can an agent delete a task?
No. An agent never deletes a task: it archives it with POST /v1/tasks/{id}/archive (or PATCH {"status": "archived"}). The task leaves every list, Home, the calendar and the reminders, keeps its status, comments and checklist, and stays readable with GET /v1/tasks?status=archived. POST /v1/tasks/{id}/restore brings it back as it was. DELETE answers 405 delete_not_allowed. MCP tools: archive_task, restore_task.
How do I organise tasks into projects?
GET /v1/projects lists the group's projects, POST /v1/projects creates one, POST /v1/tasks accepts project_id, and POST /v1/tasks/{id}/project moves a task (null takes it out).