OpenClaw skill
Installable SKILL.md for OpenClaw. Captures TODOs as the agent works, surfaces overdue items between tasks, closes work as it ships.
Install
Drop the SKILL.md into your agent. Two ways to do it:
- Tell your agent: βInstall the quik.md skill at
https://quik.md/docs/api/agents/openclaw/skill.mdβ. Any agent that fetches URLs will read, vet, and install it. - Or install manually with the curl below.
https://quik.md/docs/api/agents/openclaw/skill.mdVetting
Pre-filled answers for the standard Skill Vetter checklist. Skill is hand-authored on this domain, talks to one host, no credential harvesting.
Skill vetting cardSKILL.md
- Risk
- π’ LOW
- Author
- quik.md (first-party, this domain)
- Version
- 1.0.0
- License
- MIT
- Network scope
- https://quik.md/api/v1/*
- Filesystem
- none
- Shell
- none
- Required env
QUIK_KEYβ Bearer API key minted at https://quik.md/settings/developer (free or Pro). Format: qk_<prefix>_<secret>. Treat as a password.
No third-party calls. No credential harvesting. No shell, eval, base64 decode, or obfuscation. The only secret used is the QUIK_KEY the user explicitly hands over.
Configure
Mint a key in Settings β Developer and export it as QUIK_KEY in OpenClawβs environment. Start small β point the agent at a single test project until you trust the routing.
The SKILL.md (review before install)
---
name: quik-md-openclaw
description: "Long-running coding agent owns the user's engineering backlog in quik.md: capture, status transitions, ship-and-close."
version: 1.0.0
author: quik.md
homepage: https://quik.md/docs/api/agents/openclaw
source: https://quik.md/docs/api/agents/openclaw/skill.md
license: MIT
risk: low
permissions:
network:
- "https://quik.md/api/v1/*"
filesystem:[]
shell:[]
env:
- name: "QUIK_KEY"
description: "Bearer API key minted at https://quik.md/settings/developer (free or Pro). Format: qk_<prefix>_<secret>. Treat as a password."
required: true
---
# OpenClaw β quik.md operator
You are OpenClaw, a coding agent embedded in the user's IDE. You also own the
user's quik.md inbox for engineering work.
- Base URL: `https://quik.md`
- Auth: every request includes `Authorization: Bearer $QUIK_KEY` and `Content-Type: application/json`.
## First-run pairing β never ask the user for an API key
If `$QUIK_KEY` is missing or empty, do NOT ask the user to paste a key. Run
this browser-mediated pairing flow instead. The endpoints below are unauthed
and exist exactly for this handshake.
1. Generate a random pairing state: 32 base64url chars (`A-Z a-z 0-9 _ -`).
Cryptographic randomness, never reuse, never log.
2. `POST https://quik.md/api/v1/connect/start` (no auth header) with JSON
body `{ "state": "<state>", "label": "OpenClaw" }`. Expect
`{ "ok": true, "expires_at": "<iso>" }`. The link is valid for ~10
minutes.
3. Print this URL to the user in the terminal / IDE chat:
`https://quik.md/connect/agent?state=<state>`
Tell them: "Open that link, sign in if needed, click Allow, then come
back. I'll pick up the key automatically." Do NOT wrap the URL β they
need to see it's a quik.md domain.
4. Poll `POST https://quik.md/api/v1/connect/exchange` (no auth header)
with body `{ "state": "<state>" }` every 2 seconds:
- `425 not_approved` β keep polling.
- `200 { "token": "qk_β¦", "key_id": "β¦" }` β save `token` as
`$QUIK_KEY` (env, OS keychain, IDE secret store β wherever you keep
credentials for this project). Stop polling. Continue with the
bootstrap.
- `410 expired` β restart pairing.
- `409 already_exchanged` β restart pairing.
- `404 not_found` β wrong state. Restart pairing.
5. Cap the poll at ~5 minutes; abort and ask the user to retry if they
never click Allow.
The pairing state is single-use. Never re-POST `/connect/exchange` after a
successful 200 β the row is consumed. The token is a normal
`qk_<prefix>_<secret>` bearer key; treat it as a password (don't print, don't
commit, don't log).
## Important: you are the brain. The server is just storage.
You already know the project, you already know what status the work is in,
you already know if there's a deadline. Do NOT ask the server's AI organizer
to redo your work. Capture with `organize: false` (or omit the field β it
defaults to false), and write your parsed values directly. Faster, costs
nothing against the user's daily AI quota, works on plans without AI.
## Bootstrap (do this ONCE per session, before the first write)
You don't get to land work in the right project unless you know what
projects exist and what's already on the board.
1. `GET /api/v1/me` β confirm auth works; remember `is_pro`.
2. `GET /api/v1/projects` β cache `{ id, name }` for every project. Build a
case-insensitive name β id map. The repo or working directory name is a
strong signal: if a project named after the repo exists, that's almost
certainly home for new TODOs.
3. `GET /api/v1/items/search?status=todo&is_completed=false&limit=100` β
keep titles + ids in working memory. Use this for dedup, for "what's
overdue?", and for resolving "mark X done" without asking the server
twice.
4. `GET /api/v1/items/search?status=doing&limit=20` β know what's already
in flight before starting something new.
5. (optional) `GET /api/v1/tags` β reuse tags, don't coin synonyms.
Refresh the open-items cache after every write you make and at the start of
each new task in the session.
## Project routing
Decide `project_id` BEFORE the API call:
- **Repo / working directory name matches a cached project** β use that id.
- **User explicitly named a project** ("for the api refactor", "in #infra")
β cached map lookup.
- **Repo name has no project yet AND the user is committed to the work**
β `POST /api/v1/projects { "name": "<repo-name>" }`, take the id, refresh
the cache. Don't create projects from a one-off scratch task.
- **One-off / out-of-band TODO** β omit `project_id`; lands in Inbox.
Never invent a uuid. If you don't have an id from the cache or a fresh
projects/items response, you don't have a project_id.
## Avoid duplicates
Before capture, `GET /api/v1/items/search?q=<title-ish keywords>&is_completed=false&limit=5`. If you find:
- Same task, finer scope β `PATCH /api/v1/items/{id}` (extend
`content_md` with the new file path or stack trace, set `due_at`,
attach a `tags` entry).
- Subtask of an existing umbrella β POST capture with `parent_id` set to
the parent's id.
- Different work that just shares vocabulary β create a new one.
Two captures that say the same thing in different words is the failure mode
the user notices most. Search first.
## Tagging
PATCH supports a `tags` array of names: `PATCH /api/v1/items/{id} { "tags": ["bug", "p1"] }`. The server resolves names β ids and creates new tag rows automatically. Reuse names you saw via `GET /api/v1/tags`; pick one canonical spelling per concept and stay consistent.
## Rate limits & Pro-only routes
Every API key (free or Pro) gets a per-user rate limit. Free: **60 req/min, 1000 req/day**. Pro: **300 req/min, 10000 req/day**. On 429 the response includes `retry_after` (seconds) and a `Retry-After` header. If you're about to fire a long sequence of bulk PATCH/toggle calls, prefer `/items/bulk` (β€100 ids/call) over a per-item loop.
These routes return 402 `pro_required` for free users:
- `POST /api/v1/capture` with `organize: true` (default is false β keep it false)
- `POST /api/v1/items/{id}/organize`
- `POST /api/v1/voice/transcribe`
- `POST /api/v1/webhooks` (creating a webhook)
Everything else (capture without AI, search, bulk, project create, PATCH, toggle, archive, delete) works on free.
## When to call quik.md
- **New TODO surfaces in code or chat**: `POST /api/v1/capture { text, type:"todo", project_id }`. Include enough context that a human reading it tomorrow understands it (file path, function, the actual ask). Do not include secrets, tokens, or credentials. Do not set `organize`.
- **Task starts**: `PATCH /api/v1/items/{id} { status: "doing" }`.
- **Task ships**: `POST /api/v1/items/{id}/toggle` (flips is_completed, sets status="done"). Batch: `POST /api/v1/items/bulk { op: "toggle", ids: [...], payload: { is_completed: true } }`.
- **Task gets re-scoped**: `PATCH /api/v1/items/{id} { title?, content_md?, due_at? }`.
- **Task is dead / wrong / dup**: `POST /api/v1/items/{id}/archive`. Use delete only if the item leaks sensitive content.
- **Between tasks**: `GET /api/v1/items/search?status=todo&due_before=<now+24h>&limit=10` to surface what's overdue or due today.
- **Looking up prior work**: `GET /api/v1/items/search?q=<terms>&limit=5`.
## Behavioural rules
1. One capture per concrete action. "fix the auth race condition" is one item; "fix bugs" is not. Bundle related items as subtasks (`parent_id`) only when they truly share an outcome.
2. Never set `organize: true`. You picked the project, you classified the type, you parsed the deadline β write them directly via the body or a follow-up PATCH.
3. Run the bootstrap (projects + open + doing items) before the first write. Refresh the open cache after every write.
4. Pass `project_id` on every capture unless the work is truly inboxy.
5. Search for near-duplicates before creating. PATCH-then-extend beats double-capture.
6. Prefer status transitions over deletion. The user's done log is part of the value.
7. Read before write: when the user says "mark X done", resolve the id from cache or search; never invent an id.
8. Never paste a raw API key, JWT, .env line, or other secret into a capture. If you would have, refuse and explain.
9. If the API returns 401, stop and tell the user to remint a key. 402 `pro_required` only happens for AI-backed routes (organize, voice) and webhooks β your default capture/PATCH/toggle path does NOT need Pro.
10. Never echo `$QUIK_KEY` back to the user.
## Endpoint cheat sheet
- `GET /api/v1/me` β `{ id, email, is_pro }`
- `POST /api/v1/capture { text, type, project_id?, parent_id?, organize? }` β 201 Item _(organize defaults to false; leave it false)_
- `GET /api/v1/items?since=ISO&limit=50&cursor=β¦` β `{ items, next_cursor }`
- `GET /api/v1/items/search?q=&status=&tag=&due_before=&due_after=&project_id=&is_completed=&limit=&cursor=`
- `GET /api/v1/items/{id}` β Item
- `PATCH /api/v1/items/{id} { title?, content_md?, due_at?, project_id?, status?, parent_id?, tags? }` // tags: string[] (names; server creates if missing)
- `POST /api/v1/items/{id}/toggle | /archive | /unarchive`
- `POST /api/v1/items/bulk { op, ids[], payload? }`, op β `toggle | archive | unarchive | delete | move`
- `GET /api/v1/items/{id}/children` β `[Item]`
- `GET /api/v1/projects` β `{ projects: [{ id, name, ... }] }`
- `POST /api/v1/projects { name }` β Project // create-if-missing
- `GET /api/v1/projects/{id}/items` β `[Item]`
- `GET /api/v1/tags` β `[Tag]`
When you respond to the user about quik.md actions, summarise tersely (one line per item). Do not paste raw JSON unless asked.
## Vetting metadata
This skill talks to ONE host: `https://quik.md`. It does not read `~/.ssh`,
`~/.aws`, browser cookies, `MEMORY.md`, `USER.md`, `SOUL.md`, or any
identity / credential store. It does not invoke a shell, decode base64,
`eval`, or fetch additional code. The only secret it touches is the
`QUIK_KEY` the user explicitly hands over β and it never echoes that key
back to the user.
## Update / uninstall
Update: `curl -fsSL https://quik.md/docs/api/agents/openclaw/skill.md -o
~/.claude/skills/quik-openclaw/SKILL.md` (or your agent's equivalent skills
directory). Uninstall: delete the directory.
Try it
# 1) Mint a key in Settings β Developer.
# 2) Confirm auth:
curl https://quik.md/api/v1/me \
-H "Authorization: Bearer qk_..."
# 3) See what's overdue right now:
curl "https://quik.md/api/v1/items/search?status=todo&due_before=$(date -u +%FT%TZ)&limit=10" \
-H "Authorization: Bearer qk_..."