Skip to content

Docs

REST API

Read and change a project's tasks, requests and questions from a script, a CI job or another app, with an API token. Every endpoint, with examples.

The REST API is for a program of your own: a script, a CI job, a cron job, or another app. It reads and changes a project's lists the way the MCP tools do, and answers in JSON. Agents don't need it; they use the MCP tools.

The address is https://hamloy.com/api/v1. Every request sends an API token from the Connect page:

curl https://hamloy.com/api/v1/projects \
  -H "Authorization: Bearer $HAMLOY_TOKEN"

A token reaches every project you are on, with what you may do there. What a program changes is signed api in the history and in the agents' news ("The API ticked B-k3f9", or "Sara's api" on a shared project), so you can always tell what a program did from what you did yourself.

Projects

GET /projects lists the projects you are on:

{
  "projects": [
    { "id": "4f0c2a9e-…", "name": "Pebble", "owner": "Sara", "yours": true, "read_only": null,
      "board": "https://hamloy.com/p/4f0c2a9e-…" }
  ]
}

read_only says why, when you can read a project but not change it, such as a project your plan no longer covers.

GET /projects/:id is one project, with its lists:

{ "project": { "id": "4f0c2a9e-…", "name": "Pebble", …,
    "lists": [ { "name": "backlog", "kind": "task", "prefix": "B" }, { "name": "requests", "kind": "request", "prefix": "R" },
               { "name": "questions", "kind": "question", "prefix": "Q" }, { "name": "ideas", "kind": "own", "prefix": "I" } ] } }

Listing items

GET /projects/:id/items lists what the list tool would, in list order, subtasks right after their parent:

Parameter What it does
kind task, request, question, one of your own lists by name, or all (the default)
status open (the default), ready (open and waiting on nothing), done or all
section only items under this heading
find items with all these words in their key, title, notes or fields; done ones too
from, limit paging: 50 at a time by default, at most 200
curl "https://hamloy.com/api/v1/projects/$PROJECT/items?kind=task&status=ready" \
  -H "Authorization: Bearer $HAMLOY_TOKEN"
{
  "project": { "id": "4f0c2a9e-…", "name": "Pebble" },
  "total": 12, "from": 0, "next": null,
  "items": [
    {
      "key": "B-k3f9", "version": "B-k3f9@3", "kind": "task", "list": "backlog",
      "title": "Streak counter on the habit card", "summary": null, "section": "This week",
      "notes": "Count days in a row, not days in total.", "fields": { "area": "app" },
      "parent": null, "subtasks": [], "needs": [], "ready": true, "claimed_by": "claude",
      "done": false, "done_at": null, "done_by": null,
      "added_at": "2026-10-17T09:12:44Z", "added_by": "claude",
      "url": "https://hamloy.com/p/4f0c2a9e-…/items/B-k3f9"
    }
  ]
}

next is the from of the next page, or null on the last. Every item has the same keys, null where it has nothing. A question also has options (each with text, recommended and assumed), answer and why.

GET /projects/:id/items/:key is one item. An old key still finds an item that moved to another list.

Adding an item

POST /projects/:id/items with a JSON body:

curl https://hamloy.com/api/v1/projects/$PROJECT/items \
  -H "Authorization: Bearer $HAMLOY_TOKEN" -H "Content-Type: application/json" \
  -d '{"title": "Checkout fails for cards from Brazil", "kind": "task", "section": "Bugs",
       "summary": "Stripe declines with a currency error", "notes": "Seen 14 times since Monday.",
       "fields": {"area": "billing", "prio": "high"}}'
Field
title required, one line
kind task (the default), request, question or one of your own lists
section the heading to put it under; made if missing
summary one short line; required when you give notes
notes text, one line per note
fields names and text, such as {"prio": "high"}
needs keys it waits on
parent a task's key, to add this as its subtask
subtasks titles of subtasks to add under it
options, recommended a question's options, and the number of the one you recommend

It answers 201 with the item.

Changing an item

PATCH /projects/:id/items/:key with any of title, summary, section, fields ("" takes a field away), needs (all of them; [] for none), notes (replaces them all), add_note, list (moves it), options, recommended, before and after (a key in the same list).

Send version as you read it ("B-k3f9@3") and a change someone else made since is never overwritten: the request is refused, and you read the item again.

curl -X PATCH https://hamloy.com/api/v1/projects/$PROJECT/items/B-k3f9 \
  -H "Authorization: Bearer $HAMLOY_TOKEN" -H "Content-Type: application/json" \
  -d '{"version": "B-k3f9@3", "fields": {"prio": "high"}, "add_note": "Two more reports today."}'

Ticking, answering, deleting

  • POST /projects/:id/items/:key/tick, with an optional note: marks a task or request done. {"done": false} opens it again.
  • POST /projects/:id/items/:key/answer, with answer (an option's number or its words, or your own) and an optional why. Answering is yours, as on the board. The reply's ready_now lists what no longer waits on it.
  • DELETE /projects/:id/items/:key deletes it, with its subtasks. no_longer_waiting lists what waited on it.
  • POST /projects/:id/items/:key/restore brings it back.

Each answers with the item as it is now.

Errors

A refused request answers with a status and one sentence in error:

{ "error": "B-k3f9 changed since you read it; it's now B-k3f9@4: Streak counter on the habit card. Read it again and redo your change if it still applies" }
Status When
400 the body isn't JSON
401 no token, or one that isn't, or was revoked
403 the project is read-only for you, and error says why
404 no such project, or it isn't open to you
422 the request can't be done as asked: an unknown key, a stale version, a missing title
429 more than 100 requests in a minute with one token

Limits

  • 100 requests a minute for each token.
  • No claims: a program doesn't hold a task the way an agent does. To have work picked up, add it, and the agents take it from the lists.
  • To send work in from a service that posts its own JSON, such as an error tracker or a form, without writing code, use a project's webhook instead.