create_tasks_batch
Bulk-create tasks in a Kanboard project with a single batch request. Supports 1-100 tasks, optional field defaults, and returns per-task success or failure.
Instructions
Bulk-create tasks in a Kanboard project using a single JSON-RPC batch request. Accepts 1–100 tasks per call. Non-atomic: partial failure is possible — check failed[] for per-task errors. Optional fields (column_id, owner_id, category_id, swimlane_id) fall back to .kanboard.yaml defaults when not provided. Returns { created: [...], failed: [...] } — never throws on partial failure.
Input Schema
| Name | Required | Description | Default |
|---|---|---|---|
| project_id | No | Kanboard project id (overrides .kanboard.yaml). | |
| project_identifier | No | Kanboard project identifier string (overrides .kanboard.yaml). | |
| tasks | Yes | Array of task creation inputs. 1..100 items. Non-atomic: partial failure possible. Inspect failed[] for per-task errors. |
Implementation Reference
- src/handler/kanboard.ts:536-631 (handler)The KanboardHandler.createTasksBatch method executes the actual batch logic: builds JSON-RPC 'createTask' calls, invokes them via apiClient.batch(), and parses results into created[]/failed[] envelopes (lines 536-631).
public async createTasksBatch( projectId: number, items: BatchCreateTasksItem[], ): Promise<BatchCreateTasksResult> { if (items.length === 0) { return { created: [], failed: [] }; } if (items.length > BATCH_TASK_CAP) { throw new ValidationError( "createTasksBatch", `createTasksBatch: items count ${String(items.length)} exceeds cap ${String(BATCH_TASK_CAP)}`, ); } // Build batch calls — use index as the id for alignment. // Map id → original input index (index IS the id here, but we build // the map explicitly for clarity and future-proofing). const idToIndex = new Map<number, number>(); const calls: BatchCall[] = items.map((item, index) => { idToIndex.set(index, index); return { method: "createTask", params: { project_id: projectId, ...item }, id: index, }; }); this.#logger.debug({ method: "createTasksBatch", count: items.length }, "batch starting"); const results = await this.#apiClient.batch(calls); const created: BatchCreateTasksResult["created"] = []; const failed: BatchCreateTasksResult["failed"] = []; for (const batchResult of results) { const originalIndex = idToIndex.get(batchResult.index) ?? batchResult.index; const item = items[originalIndex]; const title = item?.title ?? ""; if (batchResult.ok) { const raw = batchResult.result; // Result can be an integer task_id or false (Kanboard returns false in // the result field for some failure cases even in batch mode) if (raw === false) { failed.push({ index: originalIndex, title, error: { code: "API_ERROR", message: "createTask returned false (pre-validate inputs)", }, }); } else if (typeof raw === "number" && raw > 0) { created.push({ index: originalIndex, task_id: raw, title }); } else if (typeof raw === "string") { const n = Number(raw); if (!isNaN(n) && n > 0) { created.push({ index: originalIndex, task_id: n, title }); } else { failed.push({ index: originalIndex, title, error: { code: "API_ERROR", message: `Unexpected result: ${raw}` }, }); } } else { failed.push({ index: originalIndex, title, error: { code: "API_ERROR", message: `Unexpected result type: ${typeof raw}` }, }); } } else { const err = batchResult.error; failed.push({ index: originalIndex, title, error: { code: err.code < 0 ? "RPC_ERROR" : "API_ERROR", message: err.message, }, }); } } // Sort by original index for deterministic output created.sort((a, b) => a.index - b.index); failed.sort((a, b) => a.index - b.index); this.#logger.debug( { method: "createTasksBatch", createdCount: created.length, failedCount: failed.length }, "batch complete", ); return { created, failed }; - src/shared/types.ts:84-121 (schema)TypeScript interfaces BatchCreateTasksItem and BatchCreateTasksResult defining the input/output shapes for create_tasks_batch.
export interface BatchCreateTasksItem { title: string; description?: string | undefined; column_id?: number | undefined; owner_id?: number | undefined; category_id?: number | undefined; swimlane_id?: number | undefined; color_id?: string | undefined; /** Unix epoch seconds — already converted from ISO 8601 input by the tool layer. */ date_due?: number | undefined; score?: number | undefined; priority?: number | undefined; reference?: string | undefined; tags?: string[] | undefined; /** Unix epoch seconds — already converted from ISO 8601 input by the tool layer. */ date_started?: number | undefined; creator_id?: number | undefined; } /** * Output shape returned by `create_tasks_batch`. * Never throws on partial failure — all outcomes are in created[] or failed[]. */ export interface BatchCreateTasksResult { created: { index: number; task_id: number; title: string; }[]; failed: { index: number; title: string; error: { code: string; message: string; }; }[]; } - src/shared/constants.ts:46-53 (helper)BATCH_TASK_CAP constant = 100, the max number of tasks allowed in a single create_tasks_batch call. Used by both the Zod schema and the handler.
/** * Maximum number of items allowed in a single `create_tasks_batch` call. * * SPEC CONTRACT (OQ-04 resolution): this is 100 per spec FR-14 (1..100). * The design artifact §1f originally had 50 — the spec wins. * If you change this value, the constants.test.ts assertion will fail loudly. */ export const BATCH_TASK_CAP = 100;