# Time Entries API

All examples use:

```text
https://app.keito.ai/api/v2
```

Add `Authorization: Bearer <kto_...>` and `Keito-Account-Id: <company_id>` to every API key request.

## Create a Time Entry

`POST /api/v2/time_entries`

Creates a time entry. To start a running timer, set `is_running` to `true`.

### Request Body

| Field | Type | Required | Description |
|---|---|---|---|
| `project_id` | string | Yes | Project ID |
| `task_id` | string | Yes | Task ID |
| `spent_date` | string | Yes | Date of work (`YYYY-MM-DD`) |
| `hours` | number | No | Duration in decimal hours for completed entries |
| `is_running` | boolean | No | Create a running timer |
| `started_time` | string | No | Start time (`HH:mm`) in the workspace timezone |
| `ended_time` | string | No | End time (`HH:mm`) in the workspace timezone for completed entries |
| `notes` | string | No | Description of work |
| `billable` | boolean | No | Override billable status |
| `source` | string | No | `web`, `cli`, `api`, or `agent` |
| `metadata` | object | No | JSON object, max 4KB |

When `is_running` is `true`, omitting `started_time` starts the timer at the current time. To create a running timer that started in the past, send the past `spent_date` and optional `started_time`; the workspace must have **Past timer starts** enabled in [Company Settings](/docs/settings/company-settings). Future start times are rejected.

### Example Request

```bash
curl -X POST https://app.keito.ai/api/v2/time_entries \
  -H "Authorization: Bearer kto_xxxxx" \
  -H "Keito-Account-Id: your_company_id" \
  -H "Content-Type: application/json" \
  -d '{
    "project_id": "project_id_here",
    "task_id": "task_id_here",
    "spent_date": "2026-05-05",
    "hours": 1.5,
    "notes": "Refactored authentication module",
    "source": "api",
    "metadata": {
      "session_id": "550e8400-e29b-41d4-a716-446655440000"
    }
  }'
```

## List Time Entries

`GET /api/v2/time_entries`

Returns a paginated list of time entries.

### Query Parameters

| Parameter | Type | Description |
|---|---|---|
| `page` | number | Page number |
| `per_page` | number | Results per page |
| `source` | string | Filter by source |
| `project_id` | string | Filter by project |
| `task_id` | string | Filter by task |
| `user_id` | string | Filter by user |
| `client_id` | string | Filter by client |
| `from` | string | Start date, inclusive |
| `to` | string | End date, inclusive |
| `is_billed` | boolean | Filter billed status |
| `is_running` | boolean | Filter running timers |
| `updated_since` | string | ISO timestamp lower bound |

### Example Request

```bash
curl "https://app.keito.ai/api/v2/time_entries?source=cli&from=2026-05-01&to=2026-05-31" \
  -H "Authorization: Bearer kto_xxxxx" \
  -H "Keito-Account-Id: your_company_id"
```

### Example Response Shape

```json
{
  "time_entries": [],
  "per_page": 100,
  "total_pages": 0,
  "total_entries": 0,
  "page": 1,
  "links": {
    "first": "/api/v2/time_entries?page=1&per_page=100",
    "next": null,
    "previous": null,
    "last": "/api/v2/time_entries?page=1&per_page=100"
  }
}
```

## Update a Time Entry

`PATCH /api/v2/time_entries/:id`

Updates editable fields on a time entry. `source` is immutable after creation.

```bash
curl -X PATCH https://app.keito.ai/api/v2/time_entries/time_entry_id \
  -H "Authorization: Bearer kto_xxxxx" \
  -H "Keito-Account-Id: your_company_id" \
  -H "Content-Type: application/json" \
  -d '{
    "hours": 1.5,
    "notes": "Completed OAuth implementation"
  }'
```

## Stop a Running Timer

`PATCH /api/v2/time_entries/:id/stop`

Stops a running time entry and calculates elapsed duration server-side.

```bash
curl -X PATCH https://app.keito.ai/api/v2/time_entries/time_entry_id/stop \
  -H "Authorization: Bearer kto_xxxxx" \
  -H "Keito-Account-Id: your_company_id" \
  -H "Content-Type: application/json" \
  -d '{"notes": "Finished implementation"}'
```

## Delete a Time Entry

`DELETE /api/v2/time_entries/:id`

Deletes a time entry. Approved or locked entries cannot be deleted.

```bash
curl -X DELETE https://app.keito.ai/api/v2/time_entries/time_entry_id \
  -H "Authorization: Bearer kto_xxxxx" \
  -H "Keito-Account-Id: your_company_id"
```

Returns HTTP `204 No Content` on success.