← Receivers

tasks:board

tasks family

mcp

The whole task board in ONE request: every task in a group (or its whole subtree with scope:'tree'), filtered in RAM, with `total` before paging, a `nextCursor` when there is more, `truncated` naming any budget that bit, and a `summary` over ALL matched rows — counts by status/tag/assignee/workspace, the ready set, the blocked set, overdue, unassigned, orphans, no-notes. Use this to plan; use tasks:bulk to act on what it shows.

Effect
ask
Awaits an outcome — the call returns the response below.
Caller
member
The class of authority the caller must already hold, decided from the attested context with no round trip.
Reversible
yes
The effect can be undone by a later call.
Idempotent
yes
Safe to retry as-is.

Send it with your agent

One click hands your coding agent a prompt that registers the substrate, reads this contract, and makes the call. Launch opens the app; the others copy the prompt.

Claude Code
Codex
Cursor
Gemini CLI
Claude Desktop
ChatGPT
curl -X POST https://one.ie/api/ask/tasks:board \
  -H "Authorization: Bearer $ONE_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"data": { "workspace": "one", "view": "summary" }}'

The key is never in a link. npx -y @oneie/cli login writes it to ~/.config/oneie/key on your machine.

Request

Validated before dispatch — an invalid payload is refused with the fix, never half-applied.

  • workspace string optional Group slug. Omit for your own. A scope REQUEST — refused ({ok:false, error:'forbidden', workspace}) when you may not read it, never swapped for your own.
  • scope own | tree optional tree = this group AND every descendant group (the CEO / agency lens). Default own.
  • status string | string[] optional open | blocked | picked | done | verified | failed | dissolved, one or many. 'active' = open+blocked+picked (the default). 'all' = every status.
  • tags string[] optional Row must carry EVERY tag (AND).
  • anyTags string[] optional Row must carry AT LEAST ONE of these tags (OR).
  • assignee string optional Actor slug. '' = unassigned only.
  • parent string optional Only direct children of this task id. '' = top-level rows (no parent).
  • search string optional Case-insensitive substring on the task name.
  • tid string optional Exactly this task id (any `task:` prefix doubling ignored). How a `?task=<tid>` link opens a row that is not on the first page.
  • ready boolean optional true = only rows claimable now (open, and every blocker closed). false = only rows that are NOT claimable now (not open, or at least one blocker still open). Omit for both.
  • include notes | graph | dates | thread[] optional Widen each row. notes = the prose goal, WHOLE and unclipped (it was silently cut at 2000 chars until 2026-09-21, mid-word, while `truncated` named the tag budget beside it and stayed silent about this one; the escape hatch its comment named, tasks:get, was never built, so no door returned a longer note at all). Pass `notesClip` for a bound and the response reports `truncated.notes`; graph = children + blocks (the reverse edges); dates = startAt/createdAt/closedAt; thread = the task's inbox conversation (id, comment count, last message). Compact rows already carry parent, blockedBy, dueAt.
  • notesClip number optional Opt-in per-row ceiling on `include:'notes'` prose. Omitted = WHOLE notes, the default. When it bites, the response carries `truncated.notes` = the number of rows cut, so a clipped board can never read as complete. Values under 200 are ignored rather than honoured — a ceiling that cuts every row is a request for a different field, not a budget.
  • view rows | summary | both optional summary = counts and sets only, no rows (cheapest way to see a 2,000-row board). Default both.
  • sort priority | due-at | updated-at | … optional Default priority (desc).
  • dir asc | desc optional
  • limit number optional Rows per page. Default 500, max 2000.
  • cursor string optional The nextCursor from the previous page.
  • fresh boolean optional Rebuild the snapshot instead of reading the memo. Rate-limited to once per memo window.

Response

What comes back from the call.

  • ok boolean
  • workspace string
  • workspaces string[] Every group the rows were read from (scope:'tree').
  • asOf string ISO time the snapshot was built. Rows are at most this stale — unless `stale` is present, when they are exactly this stale. Past the 60s memo a read is answered from the last board built, never rebuilt inside the request; a five-minute cron rebuilds every board read in the last hour. So `asOf` is usually minutes old, but the first read after a quiet spell can return a board up to 7 days old (the last-good TTL), and it says so only through `asOf`. A board that has been read for 15 minutes without a refresh landing comes back `stale` with `upstream.detail: "refresh_overdue"`. `fresh:true` (or a tasks:bulk write) makes the next read rebuild.
  • cached boolean
  • stale true Present only when the rebuild FAILED (or the background refresh is overdue — `upstream.detail: "refresh_overdue"`) and this answer is the last board that was successfully built (kept up to 7 days). `asOf` is when that board was built; `upstream` is why the rebuild failed. Absent = a live snapshot. tasks:bulk never acts on a stale board.
  • upstream object Why the rebuild failed. Present iff `stale`.
  • total number Rows matching the filters, before paging. Exact — UNLESS `truncated.rows` is present, in which case the snapshot itself was capped and total is a floor.
  • returned number
  • nextCursor string Present iff more rows match. Absent = you have them all.
  • truncated object Absent = nothing was cut. Present = say so before you plan on it.
  • summary object
  • tasks object[]
  • error string

Traffic

Every call to tasks:board, counted where it is dispatched — over HTTP or in-process alike. Aggregate only — no actor, no payload, no workspace.

Counting…

Wiring

Every place in the open source that names tasks:board, and the file that answers it. Read from the tree at build time — a receiver is reached by NAME through one door, so there is no import edge to follow and a grep is the honest shape of the question. Structure, not volume — the count is in Traffic above.

Called from

UI 4
  • one.ie/web/src/components/in/NextHundred.tsx:160
  • one.ie/web/src/components/in/TasksPane.tsx:271
  • one.ie/web/src/components/in/top-hundred.ts:5
  • one.ie/web/src/components/in/TopHundred.tsx:147
API route 8
  • one.ie/web/src/lib/dev-read-proxy.ts:3
  • one.ie/web/src/lib/ladder.ts:147
  • one.ie/web/src/lib/promise/front-read.ts:58
  • one.ie/web/src/lib/receiver-index.generated.ts:319
  • one.ie/web/src/lib/resolvers/_shared.ts:166
  • one.ie/web/src/lib/substrate.ts:48
  • one.ie/web/src/lib/tasks/board.ts:5
  • one.ie/web/src/lib/tasks/learning.ts:6
Agent runtime 1
  • channels/src/tools/tasks.ts:139
MCP 1
  • tasks_stake · packages/mcp/src/tools/tasks.ts:361
CLI 3
  • packages/cli/src/lib/tasks-digest.ts:1
  • packages/cli/src/lib/tasks-graph.ts:3
  • packages/cli/src/tasks.ts:311
SDK 2
  • packages/sdk/src/client.ts:903
  • packages/sdk/src/tasks-bulk.ts:3
Docs 1
  • one.ie/web/src/lib/CLAUDE.md:62
→
JSON Schema
{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "type": "object",
  "properties": {
    "workspace": {
      "description": "Group slug. Omit for your own. A scope REQUEST — refused ({ok:false, error:'forbidden', workspace}) when you may not read it, never swapped for your own.",
      "type": "string"
    },
    "scope": {
      "description": "tree = this group AND every descendant group (the CEO / agency lens). Default own.",
      "type": "string",
      "enum": [
        "own",
        "tree"
      ]
    },
    "status": {
      "description": "open | blocked | picked | done | verified | failed | dissolved, one or many. 'active' = open+blocked+picked (the default). 'all' = every status.",
      "anyOf": [
        {
          "type": "string"
        },
        {
          "type": "array",
          "items": {
            "type": "string"
          }
        }
      ]
    },
    "tags": {
      "description": "Row must carry EVERY tag (AND).",
      "type": "array",
      "items": {
        "type": "string"
      }
    },
    "anyTags": {
      "description": "Row must carry AT LEAST ONE of these tags (OR).",
      "type": "array",
      "items": {
        "type": "string"
      }
    },
    "assignee": {
      "description": "Actor slug. '' = unassigned only.",
      "type": "string"
    },
    "parent": {
      "description": "Only direct children of this task id. '' = top-level rows (no parent).",
      "type": "string"
    },
    "search": {
      "description": "Case-insensitive substring on the task name.",
      "type": "string"
    },
    "tid": {
      "description": "Exactly this task id (any `task:` prefix doubling ignored). How a `?task=<tid>` link opens a row that is not on the first page.",
      "type": "string"
    },
    "ready": {
      "description": "true = only rows claimable now (open, and every blocker closed). false = only rows that are NOT claimable now (not open, or at least one blocker still open). Omit for both.",
      "type": "boolean"
    },
    "include": {
      "description": "Widen each row. notes = the prose goal, WHOLE and unclipped (it was silently cut at 2000 chars until 2026-09-21, mid-word, while `truncated` named the tag budget beside it and stayed silent about this one; the escape hatch its comment named, tasks:get, was never built, so no door returned a longer note at all). Pass `notesClip` for a bound and the response reports `truncated.notes`; graph = children + blocks (the reverse edges); dates = startAt/createdAt/closedAt; thread = the task's inbox conversation (id, comment count, last message). Compact rows already carry parent, blockedBy, dueAt.",
      "type": "array",
      "items": {
        "type": "string",
        "enum": [
          "notes",
          "graph",
          "dates",
          "thread"
        ]
      }
    },
    "notesClip": {
      "description": "Opt-in per-row ceiling on `include:'notes'` prose. Omitted = WHOLE notes, the default. When it bites, the response carries `truncated.notes` = the number of rows cut, so a clipped board can never read as complete. Values under 200 are ignored rather than honoured — a ceiling that cuts every row is a request for a different field, not a budget.",
      "type": "number"
    },
    "view": {
      "description": "summary = counts and sets only, no rows (cheapest way to see a 2,000-row board). Default both.",
      "type": "string",
      "enum": [
        "rows",
        "summary",
        "both"
      ]
    },
    "sort": {
      "description": "Default priority (desc).",
      "type": "string",
      "enum": [
        "priority",
        "due-at",
        "updated-at",
        "created-at",
        "name"
      ]
    },
    "dir": {
      "type": "string",
      "enum": [
        "asc",
        "desc"
      ]
    },
    "limit": {
      "description": "Rows per page. Default 500, max 2000.",
      "type": "integer",
      "minimum": 1,
      "maximum": 2000
    },
    "cursor": {
      "description": "The nextCursor from the previous page.",
      "type": "string"
    },
    "fresh": {
      "description": "Rebuild the snapshot instead of reading the memo. Rate-limited to once per memo window.",
      "type": "boolean"
    }
  }
}
{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "type": "object",
  "properties": {
    "ok": {
      "type": "boolean"
    },
    "workspace": {
      "type": "string"
    },
    "workspaces": {
      "description": "Every group the rows were read from (scope:'tree').",
      "type": "array",
      "items": {
        "type": "string"
      }
    },
    "asOf": {
      "description": "ISO time the snapshot was built. Rows are at most this stale — unless `stale` is present, when they are exactly this stale. Past the 60s memo a read is answered from the last board built, never rebuilt inside the request; a five-minute cron rebuilds every board read in the last hour. So `asOf` is usually minutes old, but the first read after a quiet spell can return a board up to 7 days old (the last-good TTL), and it says so only through `asOf`. A board that has been read for 15 minutes without a refresh landing comes back `stale` with `upstream.detail: \"refresh_overdue\"`. `fresh:true` (or a tasks:bulk write) makes the next read rebuild.",
      "type": "string"
    },
    "cached": {
      "type": "boolean"
    },
    "stale": {
      "description": "Present only when the rebuild FAILED (or the background refresh is overdue — `upstream.detail: \"refresh_overdue\"`) and this answer is the last board that was successfully built (kept up to 7 days). `asOf` is when that board was built; `upstream` is why the rebuild failed. Absent = a live snapshot. tasks:bulk never acts on a stale board.",
      "type": "boolean",
      "const": true
    },
    "upstream": {
      "description": "Why the rebuild failed. Present iff `stale`.",
      "type": "object",
      "properties": {
        "status": {
          "type": "number",
          "description": "HTTP status of the failed TypeDB read (0 = no response)."
        },
        "detail": {
          "description": "The gateway's error text, first 200 chars.",
          "type": "string"
        }
      },
      "required": [
        "status"
      ]
    },
    "total": {
      "description": "Rows matching the filters, before paging. Exact — UNLESS `truncated.rows` is present, in which case the snapshot itself was capped and total is a floor.",
      "type": "number"
    },
    "returned": {
      "type": "number"
    },
    "nextCursor": {
      "description": "Present iff more rows match. Absent = you have them all.",
      "type": "string"
    },
    "truncated": {
      "description": "Absent = nothing was cut. Present = say so before you plan on it.",
      "type": "object",
      "properties": {
        "rows": {
          "description": "The snapshot hit its row budget; this many unique rows were read. total is a floor.",
          "type": "number"
        },
        "edges": {
          "description": "parent/blockedBy edges were capped — a row may read as unblocked or orphaned when it is not.",
          "type": "boolean"
        },
        "tags": {
          "description": "This many returned rows may be missing tags.",
          "type": "number"
        },
        "thread": {
          "description": "include:'thread' only — this many returned rows could not have their comments counted (thread.unread present, thread.comments null). Absent = every count is real.",
          "type": "number"
        }
      }
    },
    "summary": {
      "type": "object",
      "properties": {
        "byStatus": {
          "type": "object",
          "propertyNames": {
            "type": "string"
          },
          "additionalProperties": {
            "type": "number"
          }
        },
        "byTag": {
          "type": "object",
          "propertyNames": {
            "type": "string"
          },
          "additionalProperties": {
            "type": "number"
          },
          "description": "Bare tags only (no workspace:/slug:/@ namespaces), top 100 by count."
        },
        "byAssignee": {
          "type": "object",
          "propertyNames": {
            "type": "string"
          },
          "additionalProperties": {
            "type": "number"
          },
          "description": "'' key = KNOWN unassigned. Rows whose assignee is unknown (see assigneeUnknown) are in no key."
        },
        "byWorkspace": {
          "type": "object",
          "propertyNames": {
            "type": "string"
          },
          "additionalProperties": {
            "type": "number"
          }
        },
        "ready": {
          "type": "number",
          "description": "Open with every blocker closed — claimable now."
        },
        "blocked": {
          "type": "number",
          "description": "Has at least one open blocker, or status blocked."
        },
        "overdue": {
          "type": "number"
        },
        "unassigned": {
          "type": "number",
          "description": "Rows with no @assignee tag. A FLOOR when `assigneeUnknown` is present: those rows may be assigned."
        },
        "assigneeUnknown": {
          "description": "Rows whose tag read was cut (truncated.tags) and carried no @assignee in the part that was read — UNKNOWN, not unassigned. Absent = every assignee is known.",
          "type": "number"
        },
        "orphans": {
          "type": "number",
          "description": "No parent — unreachable from any plan's containment walk."
        },
        "noNotes": {
          "type": "number",
          "description": "No prose goal — the rows a puller cannot act on."
        },
        "readyIds": {
          "type": "array",
          "items": {
            "type": "string"
          },
          "description": "Top 50 ready rows by priority."
        },
        "blockedIds": {
          "type": "array",
          "items": {
            "type": "string"
          },
          "description": "Top 50 blocked rows by priority."
        },
        "duplicateNames": {
          "description": "Exact-name collisions over EVERY matched row: normalised title (trimmed, lowercased, whitespace runs collapsed to one space) → its tids, sorted, only where more than one distinct row shares it. Largest group first, top 100 groups. {} = no collisions. A FLOOR when `truncated.rows` is present — rows past the snapshot's cut were never compared. Absent only from a server that predates the field.",
          "type": "object",
          "propertyNames": {
            "type": "string"
          },
          "additionalProperties": {
            "type": "array",
            "items": {
              "type": "string"
            }
          }
        },
        "duplicateNamesOmitted": {
          "description": "Collision groups past the 100-group cap on duplicateNames. Absent = nothing was cut.",
          "type": "number"
        }
      },
      "required": [
        "byStatus",
        "byTag",
        "byAssignee",
        "byWorkspace",
        "ready",
        "blocked",
        "overdue",
        "unassigned",
        "orphans",
        "noNotes",
        "readyIds",
        "blockedIds"
      ]
    },
    "tasks": {
      "type": "array",
      "items": {
        "type": "object",
        "properties": {
          "tid": {
            "type": "string"
          },
          "name": {
            "type": "string"
          },
          "status": {
            "type": "string"
          },
          "priority": {
            "type": "number"
          },
          "tags": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "Bare tags; workspace:/@assignee are lifted into their own fields."
          },
          "assignee": {
            "type": "string"
          },
          "assigneeUnknown": {
            "description": "This row's tags were cut before any @assignee was seen: absent `assignee` means UNKNOWN here, not unassigned.",
            "type": "boolean",
            "const": true
          },
          "workspace": {
            "type": "string"
          },
          "parent": {
            "type": "string"
          },
          "blockedBy": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "openBlockers": {
            "description": "How many of blockedBy are not yet done/verified/dissolved.",
            "type": "number"
          },
          "ready": {
            "type": "boolean"
          },
          "dueAt": {
            "type": "string"
          },
          "overdue": {
            "type": "boolean"
          },
          "startAt": {
            "type": "string"
          },
          "createdAt": {
            "type": "string"
          },
          "closedAt": {
            "type": "string"
          },
          "notes": {
            "type": "string"
          },
          "children": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "blocks": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "thread": {
            "type": "object",
            "properties": {
              "threadId": {
                "description": "The inbox thread id; absent = nobody has commented yet.",
                "type": "string"
              },
              "comments": {
                "anyOf": [
                  {
                    "type": "number"
                  },
                  {
                    "type": "null"
                  }
                ],
                "description": "Messages on the task's thread, summed over both stored key shapes (task:<id> and legacy task:task:<id>). 0 = the read succeeded and nobody has commented. null = COULD NOT LOOK — `unread` says why; never read it as 0."
              },
              "unread": {
                "description": "Present iff the count could not be read: 'd1_error' (the D1 read failed) or 'no_db' (no D1 binding). Counted in `truncated.thread`.",
                "type": "string"
              },
              "lastAt": {
                "type": "string"
              },
              "lastAuthor": {
                "type": "string"
              },
              "lastBody": {
                "description": "First 280 chars of the latest message.",
                "type": "string"
              }
            },
            "required": [
              "comments"
            ]
          }
        },
        "required": [
          "tid",
          "name",
          "status",
          "priority",
          "tags",
          "workspace",
          "ready"
        ]
      }
    },
    "error": {
      "type": "string"
    }
  },
  "required": [
    "ok"
  ]
}