Node SDK: Time Entries

Create a Time Entry

const entry = await keito.timeEntries.create({
  project_id: 'prj_abc',
  task_id: 'tsk_001',
  spent_date: '2026-03-06',
  hours: 1.5,
  notes: 'Refactored authentication module',
  source: 'api',
  metadata: {
    agent_id: 'review-bot-01',
    agent_type: 'claude-code',
    session_id: crypto.randomUUID(),
    model: 'claude-opus-4-6',
  },
});

Start a Running Timer

const timer = await keito.timeEntries.startTimer({
  project_id: 'prj_abc',
  task_id: 'tsk_001',
  spent_date: '2026-10-30',
  source: 'api',
  replace_running: true,
  metadata: {
    agent_id: 'review-bot-01',
    session_id: crypto.randomUUID(),
  },
 });
const timerId = timer.id;
if (!timerId) throw new Error('Timer response has no ID; reconcile before retrying.');

Use the work date in the workspace’s timezone. Omit started_time for a current, exact server-side start. started_time and ended_time are HH:mm workspace-time strings, not ISO timestamps. If another timer is already running, the API returns 409 Conflict unless you pass replace_running: true.

Stop a Timer

await keito.timeEntries.stopTimer(timerId, {
  notes: 'Completed code review',
});

The API calculates elapsed duration server-side from the timer start state.

Restart a Timer

await keito.timeEntries.restartTimer(timerId, {
  replace_running: true,
});

List Time Entries

// API entries for a project in the selected period
const entries = await keito.timeEntries.list({
  source: 'api',
  project_id: 'prj_abc',
  from: '2026-03-01',
  to: '2026-03-06',
});

for (const entry of entries.data) {
  console.log(`${entry.spent_date}: ${entry.hours}h — ${entry.notes}`);
}

Pagination

let page = await keito.timeEntries.list({
  project_id: 'prj_abc',
  page: 1,
  per_page: 50,
});

while (true) {
  for (const entry of page.data) {
    console.log(entry.id);
  }

  if (!page.hasNextPage()) break;
  page = await page.nextPage();
}

Delete a Time Entry

await keito.timeEntries.delete('te_abc123');