Vikunja Skill
Connects an OpenClaw agent to any Vikunja instance via its REST API (/api/v1).
Uses an API token for authentication — no session management needed.
Configuration (required env vars)
| Variable |
Description |
VIKUNJA_BASE_URL |
Base URL of the instance, e.g. https://vikunja.example.com |
VIKUNJA_API_TOKEN |
API token created under Settings → API Tokens |
The agent must confirm both vars are set before making any request.
If missing, tell the user exactly which var is absent and where to create the token
(Settings → API Tokens in the Vikunja web UI).
Important API conventions
Vikunja uses non-standard HTTP verbs:
PUT = create a new resource
POST = update an existing resource
GET = read / list
DELETE = delete
All requests:
- Header:
Authorization: Bearer <VIKUNJA_API_TOKEN>
- Header:
Content-Type: application/json
- Base:
${VIKUNJA_BASE_URL}/api/v1
Paginated list endpoints accept ?page=N&per_page=50&s=<search>.
Response headers x-pagination-total-pages and x-pagination-result-count
tell you if more pages exist.
Supported operations
See references/endpoints.md for the full endpoint reference.
Projects
- List all projects the user has access to
- Create a new project
- Get a single project by ID
Tasks
- List tasks (all, or filtered by project)
- Get a single task by ID
- Create a task in a project
- Update a task (title, description, priority, due date, percent done)
- Mark a task as done (
"done": true)
- Delete a task
Labels
- List all available labels
- Create a label
- Add a label to a task
- Remove a label from a task
Assignees
- Add a user as assignee to a task
- Remove an assignee from a task
- List task assignees
Reminders
- Add a reminder to a task (absolute datetime or relative offset)
- Remove a reminder from a task
- Reminders are part of the task object — use the task update flow
Task Relations
- Create a relation between two tasks (precedes, follows, blocked_by, subtask, etc.)
- Delete a relation
- When creating a new task with a known relation, use
related_tasks inline in the body — no separate call needed
- When adding relations to already-existing tasks, use
PUT /tasks/{id}/relations
Workflow guidelines
- Resolve names to IDs first. If the user says "add a task to my Work
project", list projects and find the ID for "Work" before creating the task.
- Confirm destructive actions. Before deleting a task or project, confirm
with the user.
- Show structured output. When listing tasks, present title, due date,
priority, labels, and done status. Format dates in a human-readable way.
- Handle pagination. If
x-pagination-total-pages > 1, fetch subsequent
pages or inform the user that results are truncated.
- Error handling. On HTTP 4xx/5xx, surface the
message field from the
JSON response to the user. On 401, remind them to check VIKUNJA_API_TOKEN.
Smart task creation
When the user asks to create a task, follow this enrichment workflow before
making any write requests.
Step 1 — Gather context (parallel where possible)
- Resolve project name → ID (GET /projects if needed)
- GET /projects/{projectID}/tasks?per_page=50 — fetch existing tasks so you
can spot related work, naming conventions, and priority patterns
- GET /labels?per_page=50 — fetch available labels so you can suggest matching ones
Step 2 — Infer task fields
Using the user's request and the context collected above, reason about:
| Field |
How to infer |
| title |
Clean, imperative phrasing. Extract from the request. |
| description |
Only if the task is non-trivial. Write a short summary and, where applicable, a markdown checklist (- [ ] step) for action items. |
| priority |
Match signal words: "urgent"/"asap"/"critical" → 4-5; "important"/"soon" → 3; "when you can"/"low" → 1; otherwise leave at 0 (none). |
| labels |
Match against existing labels by keyword similarity. Never create new labels silently — propose only existing ones. |
| due_date |
Parse explicit deadlines/dates in the request ("due Friday", "by end of month"). Convert to UTC ISO-8601. If ambiguous (e.g. "soon"), ask the user rather than guessing. |
| start_date |
Set only when the user explicitly mentions a start date or "beginning on X". Do not infer from context alone — if it seems relevant but wasn't stated, ask. |
| relations |
Scan existing tasks for titles or IDs mentioned in the request. Infer relation kind: "after X" → follows, "before X" → precedes, "part of X" → subtask, "blocks X" → blocking, "related to X" → related. |
Simplicity rule: If the request is a single, unambiguous action with no
implied context (e.g. "add a task called 'Buy milk'"), skip enrichment entirely
and create the task with just the title. Do not over-engineer simple requests.
Step 3 — Confirm if enriched
If you inferred any of the following beyond the raw title, present a
structured proposal and ask the user to confirm or adjust before creating:
- a non-empty description
- a priority > 0
- one or more labels
- a due date or start date
- any relations
Present the proposal in this format:
**New task proposal**
Title: <title>
Project: <project name>
Priority: <level or none>
Start: <date or none>
Due: <date or none>
Labels: <list or none>
Relations: <list or none>
Description:
<description or none>
Shall I create it as above, or would you like to change anything?
Date rules:
- If the user gives an explicit date → parse and include it, no need to ask.
- If the user uses vague time language ("soon", "eventually") → ask for a
concrete date rather than guessing. Do not silently skip if context strongly
implies urgency.
- If no date is mentioned and context doesn't call for one → omit both fields.
If the user confirms (or the task is simple and no confirmation is needed),
proceed to Step 4.
Step 4 — Create and apply
- PUT /projects/{projectID}/tasks — with title, description, priority,
due_date, and
related_tasks inline (for relations to existing tasks)
- For each confirmed label: PUT /tasks/{newTaskID}/labels with
{"label_id": N}
- Report back: task title, its assigned ID/index, and a link if possible.
Labels cannot be set during creation — they must be applied via separate
PUT /tasks/{id}/labels calls after the task is created.
Safe task update — mandatory pattern
Vikunja's POST /tasks/{id} resets fields to zero/null if they are absent
from the body — including percent_done, due_date, start_date,
end_date, priority, hex_color, and description.
Always follow this pattern before updating any existing task:
GET /tasks/{id} — fetch current state
Build update body by starting from the full current object and overriding
only the fields you intend to change
Always preserve these fields from the GET response (even if not changing them):
- description
- done
- done_at
- due_date
- reminders
- repeat_after / repeat_mode
- priority
- start_date
- end_date
- assignees
- hex_color
- percent_done
- cover_image_attachment_id
- is_favorite
See: https://github.com/go-vikunja/vikunja/issues/1459
POST /tasks/{id} with the merged body
Never send a partial update body without first reading the task.
This applies when bulk-updating multiple tasks too: GET each one individually
before POSTing.
Example interactions
- "What tasks are due this week?" → GET /tasks/all with filter, format results
- "Mark task 42 as done" → POST /tasks/42 with
{"done": true}
- "Add the 'urgent' label to task 17" → resolve label ID, PUT /tasks/17/labels
- "Assign me to task 5" → look up current user via GET /user, PUT /tasks/5/assignees
- "Set a reminder for task 8 in 2 hours" → compute absolute datetime, POST /tasks/8
Smart creation examples
"Add a task called 'Buy milk' to Errands" → simple, no enrichment needed.
Create directly with just the title.
"In the Backend project add a task to migrate the auth service to JWT" →
Fetch project tasks + labels. Notice existing auth-related tasks. Infer:
title "Migrate auth service to JWT", description with checklist (design
schema, update endpoints, write tests, update docs), priority medium (no
urgency signal), labels matching "backend"/"auth" if available. Present
proposal, wait for confirmation, then create + apply labels.
"Add an urgent task in DevOps to fix the broken CI pipeline, it's blocking
the release" → priority = urgent (5), infer relation blocking to any
release-related task found in the project. Include a short description.
Present proposal for confirmation before creating.
"Create a task for next week Monday to review Q2 roadmap in Planning" →
Parse "next week Monday" → set due_date. No start_date implied. If labels
like "review" or "planning" exist, suggest them. Present proposal.
Read references/endpoints.md for exact request/response shapes.
1---2name: vikunja3description: Interact with a Vikunja task management instance via its REST API. Use this skill whenever the user wants to manage tasks, projects, labels, assignees, or reminders in Vikunja — including creating tasks, listing what's due, marking things done, adding labels, assigning users, or organizing projects. Trigger on phrases like "add a task", "what's due today", "create a project in Vikunja", "assign this to me", "set a reminder", or any mention of Vikunja task management.4---56# Vikunja Skill78Connects an OpenClaw agent to any Vikunja instance via its REST API (`/api/v1`).9Uses an API token for authentication — no session management needed.1011## Configuration (required env vars)1213| Variable | Description |14|---|---|15| `VIKUNJA_BASE_URL` | Base URL of the instance, e.g. `https://vikunja.example.com` |16| `VIKUNJA_API_TOKEN` | API token created under Settings → API Tokens |1718The agent must confirm both vars are set before making any request.19If missing, tell the user exactly which var is absent and where to create the token20(Settings → API Tokens in the Vikunja web UI).2122## Important API conventions2324> **Vikunja uses non-standard HTTP verbs:**25> - `PUT` = **create** a new resource26> - `POST` = **update** an existing resource27> - `GET` = read / list28> - `DELETE` = delete2930All requests:31- Header: `Authorization: Bearer <VIKUNJA_API_TOKEN>`32- Header: `Content-Type: application/json`33- Base: `${VIKUNJA_BASE_URL}/api/v1`3435Paginated list endpoints accept `?page=N&per_page=50&s=<search>`.36Response headers `x-pagination-total-pages` and `x-pagination-result-count`37tell you if more pages exist.3839## Supported operations4041See `references/endpoints.md` for the full endpoint reference.4243### Projects4445- List all projects the user has access to46- Create a new project47- Get a single project by ID4849### Tasks5051- List tasks (all, or filtered by project)52- Get a single task by ID53- Create a task in a project54- Update a task (title, description, priority, due date, percent done)55- Mark a task as done (`"done": true`)56- Delete a task5758### Labels5960- List all available labels61- Create a label62- Add a label to a task63- Remove a label from a task6465### Assignees6667- Add a user as assignee to a task68- Remove an assignee from a task69- List task assignees7071### Reminders7273- Add a reminder to a task (absolute datetime or relative offset)74- Remove a reminder from a task75- Reminders are part of the task object — use the task update flow7677### Task Relations78- Create a relation between two tasks (precedes, follows, blocked_by, subtask, etc.)79- Delete a relation80- When creating a new task with a known relation, use `related_tasks` inline in the body — no separate call needed81- When adding relations to already-existing tasks, use `PUT /tasks/{id}/relations`8283## Workflow guidelines84851. **Resolve names to IDs first.** If the user says "add a task to my Work86 project", list projects and find the ID for "Work" before creating the task.872. **Confirm destructive actions.** Before deleting a task or project, confirm88 with the user.893. **Show structured output.** When listing tasks, present title, due date,90 priority, labels, and done status. Format dates in a human-readable way.914. **Handle pagination.** If `x-pagination-total-pages > 1`, fetch subsequent92 pages or inform the user that results are truncated.935. **Error handling.** On HTTP 4xx/5xx, surface the `message` field from the94 JSON response to the user. On 401, remind them to check `VIKUNJA_API_TOKEN`.9596## Smart task creation9798When the user asks to create a task, follow this enrichment workflow before99making any write requests.100101### Step 1 — Gather context (parallel where possible)102103- Resolve project name → ID (GET /projects if needed)104- GET /projects/{projectID}/tasks?per_page=50 — fetch existing tasks so you105 can spot related work, naming conventions, and priority patterns106- GET /labels?per_page=50 — fetch available labels so you can suggest matching ones107108### Step 2 — Infer task fields109110Using the user's request and the context collected above, reason about:111112| Field | How to infer |113|---|---|114| **title** | Clean, imperative phrasing. Extract from the request. |115| **description** | Only if the task is non-trivial. Write a short summary and, where applicable, a markdown checklist (`- [ ] step`) for action items. |116| **priority** | Match signal words: "urgent"/"asap"/"critical" → 4-5; "important"/"soon" → 3; "when you can"/"low" → 1; otherwise leave at 0 (none). |117| **labels** | Match against existing labels by keyword similarity. Never create new labels silently — propose only existing ones. |118| **due_date** | Parse explicit deadlines/dates in the request ("due Friday", "by end of month"). Convert to UTC ISO-8601. If ambiguous (e.g. "soon"), ask the user rather than guessing. |119| **start_date** | Set only when the user explicitly mentions a start date or "beginning on X". Do not infer from context alone — if it seems relevant but wasn't stated, ask. |120| **relations** | Scan existing tasks for titles or IDs mentioned in the request. Infer relation kind: "after X" → `follows`, "before X" → `precedes`, "part of X" → `subtask`, "blocks X" → `blocking`, "related to X" → `related`. |121122**Simplicity rule:** If the request is a single, unambiguous action with no123implied context (e.g. "add a task called 'Buy milk'"), skip enrichment entirely124and create the task with just the title. Do not over-engineer simple requests.125126### Step 3 — Confirm if enriched127128If you inferred **any** of the following beyond the raw title, present a129structured proposal and ask the user to confirm or adjust before creating:130131- a non-empty description132- a priority > 0133- one or more labels134- a due date or start date135- any relations136137Present the proposal in this format:138139```140**New task proposal**141Title: <title>142Project: <project name>143Priority: <level or none>144Start: <date or none>145Due: <date or none>146Labels: <list or none>147Relations: <list or none>148Description:149<description or none>150151Shall I create it as above, or would you like to change anything?152```153154**Date rules:**155- If the user gives an explicit date → parse and include it, no need to ask.156- If the user uses vague time language ("soon", "eventually") → ask for a157 concrete date rather than guessing. Do not silently skip if context strongly158 implies urgency.159- If no date is mentioned and context doesn't call for one → omit both fields.160161If the user confirms (or the task is simple and no confirmation is needed),162proceed to Step 4.163164### Step 4 — Create and apply1651661. PUT /projects/{projectID}/tasks — with title, description, priority,167 due_date, and `related_tasks` inline (for relations to existing tasks)1682. For each confirmed label: PUT /tasks/{newTaskID}/labels with `{"label_id": N}`1693. Report back: task title, its assigned ID/index, and a link if possible.170171> **Labels cannot be set during creation** — they must be applied via separate172> PUT /tasks/{id}/labels calls after the task is created.173174### Safe task update — mandatory pattern175176> **Vikunja's POST /tasks/{id} resets fields to zero/null if they are absent177> from the body** — including percent_done, due_date, start_date,178> end_date, priority, hex_color, and description.179180**Always follow this pattern before updating any existing task:**1811821. GET /tasks/{id} — fetch current state1832. Build update body by starting **from the full current object** and overriding184 only the fields you intend to change1853. Always preserve these fields from the GET response (even if not changing them):186 - description187 - done188 - done_at189 - due_date190 - reminders191 - repeat_after / repeat_mode192 - priority193 - start_date194 - end_date195 - assignees196 - hex_color197 - percent_done198 - cover_image_attachment_id199 - is_favorite200201 > See: https://github.com/go-vikunja/vikunja/issues/14592022034. POST /tasks/{id} with the merged body204205**Never send a partial update body without first reading the task.**206This applies when bulk-updating multiple tasks too: GET each one individually207before POSTing.208209## Example interactions210211- "What tasks are due this week?" → GET /tasks/all with filter, format results212- "Mark task 42 as done" → POST /tasks/42 with `{"done": true}`213- "Add the 'urgent' label to task 17" → resolve label ID, PUT /tasks/17/labels214- "Assign me to task 5" → look up current user via GET /user, PUT /tasks/5/assignees215- "Set a reminder for task 8 in 2 hours" → compute absolute datetime, POST /tasks/8216217### Smart creation examples218219- "Add a task called 'Buy milk' to Errands" → simple, no enrichment needed.220 Create directly with just the title.221222- "In the Backend project add a task to migrate the auth service to JWT" →223 Fetch project tasks + labels. Notice existing auth-related tasks. Infer:224 title "Migrate auth service to JWT", description with checklist (design225 schema, update endpoints, write tests, update docs), priority medium (no226 urgency signal), labels matching "backend"/"auth" if available. Present227 proposal, wait for confirmation, then create + apply labels.228229- "Add an urgent task in DevOps to fix the broken CI pipeline, it's blocking230 the release" → priority = urgent (5), infer relation `blocking` to any231 release-related task found in the project. Include a short description.232 Present proposal for confirmation before creating.233234- "Create a task for next week Monday to review Q2 roadmap in Planning" →235 Parse "next week Monday" → set due_date. No start_date implied. If labels236 like "review" or "planning" exist, suggest them. Present proposal.237238Read `references/endpoints.md` for exact request/response shapes.