# Task time tracking

Records of time spent on a task — a nested resource of a task. Each record has a duration, an author, a comment, and a creation time. Base path — `/v1/tasks/:taskId/time`. All operations require an existing task (`:taskId`) on the Bitrix24 account.

Bitrix24 API: `task.elapseditem.*`
Scope: `task`

## Operations

- [Add an entry](./time/create.md) — `POST /v1/tasks/:taskId/time`
- [List entries](./time/list.md) — `GET /v1/tasks/:taskId/time`
- [Get an entry](./time/get.md) — `GET /v1/tasks/:taskId/time/:itemId`
- [Update an entry](./time/update.md) — `PATCH /v1/tasks/:taskId/time/:itemId`
- [Delete an entry](./time/delete.md) — `DELETE /v1/tasks/:taskId/time/:itemId`
- **Global list (by employee and period)** — `GET /v1/task-time?userId=&from=&to=` (see below)

## Global list — `GET /v1/task-time`

Returns time-tracking entries **across the whole Bitrix24 account** in a single request — with no need to iterate over tasks one by one. Convenient for scenarios like "how much time employee X spent in a month" when the list of tasks is not known in advance.

### Parameters

| Parameter | Type | Required | Description |
|----------|-----|:-----:|---------|
| `userId` | number | no | Employee identifier. Without the parameter — entries of all employees the key owner has access to |
| `from` | string | no | Lower bound of the period (inclusive) — by the `CREATED_DATE` field. Accepts ISO 8601 (`2026-05-01T00:00:00+00:00`) and a short date (`2026-05-01`) |
| `to` | string | no | Upper bound of the period (inclusive) — by the `CREATED_DATE` field. Format — same as `from` |
| `taskId` | number | no | Narrow the selection to a single task (an optional filter on top of `userId` / `from` / `to`) |
| `limit` | number | no | Number of entries per page (1..500, default 50) |
| `offset` | number | no | Offset in the selection (default 0). Converted into a page number for Bitrix24 |

### Example

```bash
curl "https://vibecode.bitrix24.com/v1/task-time?userId=5&from=2026-05-01&to=2026-05-31&limit=200" \
  -H "X-Api-Key: $VIBE_KEY"
```

### Response fields

| Field | Type | Description |
|------|-----|---------|
| `success` | boolean | Always `true` on success |
| `data` | array | Array of time-tracking entries. Keys in camelCase (`id`, `userId`, `taskId`, `seconds`, `minutes`, `commentText`, `source`, `createdDate`, `dateStart`, `dateStop`) |
| `meta.total` | number | Total number of entries in the selection (matching the filter) |
| `meta.limit` | number | Applied page limit |
| `meta.offset` | number | Applied offset |
| `meta.hasMore` | boolean | Whether there are more entries beyond the current page |

### Response example

```json
{
  "success": true,
  "data": [
    {
      "id": "13",
      "userId": "5",
      "taskId": "100",
      "seconds": "1800",
      "minutes": "30",
      "commentText": "Code review",
      "createdDate": "2026-05-20T10:00:00+00:00",
      "dateStart": "2026-05-20T11:00:00+00:00",
      "dateStop": "2026-05-20T11:30:00+00:00",
      "source": "2"
    }
  ],
  "meta": { "total": 247, "limit": 50, "offset": 0, "hasMore": true }
}
```

### Errors

| HTTP | Code | Description |
|------|-----|---------|
| 400 | `INVALID_USER_ID` | `userId` is not a positive integer |
| 400 | `INVALID_TASK_ID` | `taskId` is not a positive integer |
| 400 | `INVALID_OFFSET` | `offset` is not a multiple of `limit` — page-based listing requires values `0`, `limit`, `2 × limit`, and so on |
| 403 | `SCOPE_DENIED` | The API key lacks the `task` scope |
| 401 | `TOKEN_MISSING` | The API key has no configured tokens |

## Key fields

| Field | Description |
|------|---------|
| `id` | Entry identifier |
| `taskId` | Parent task ID |
| `userId` | Author. Employee list: `GET /v1/users` |
| `seconds` | Duration in seconds (the primary field — set at creation) |
| `minutes` | Duration in minutes (derived from `seconds`, filled by the system) |
| `commentText` | Comment for the entry (`""` if the comment is empty — not `null`) |
| `source` | Source: `2` — REST API (entries created through this endpoint) |
| `createdDate` | Entry creation date |
| `dateStart`, `dateStop` | Start and end of the tracked interval. Filled automatically and not editable through the current API |

## What to know before you start

1. **Response fields are in camelCase.** Time-tracking entries are returned in camelCase (`id`, `taskId`, `userId`, `seconds`, `minutes`, `commentText`, `source`, `createdDate`, `dateStart`, `dateStop`) — like task comments and the rest of the API. (The documentation previously promised UPPER_SNAKE_CASE by mistake.)
2. **Numeric values are serialized as strings.** `id`, `taskId`, `userId`, `seconds`, `minutes`, `source` arrive as strings (`"seconds": "900"`) — both in the per-task list/card and in the global `/v1/task-time`. For arithmetic, convert via `Number(value)`.
3. **`seconds` is the only unit of measurement on write.** The `seconds` parameter is required; the system recalculates `minutes` itself.
4. **Entries are deleted together with the task.** If the parent task is deleted ([`DELETE /v1/tasks/:id`](./delete.md)), its time-tracking entries become inaccessible — a separate `DELETE` for each is not required. An attempt to reference them by the former ID returns `422`.
5. **`dateStart` and `dateStop` are not editable through the API.** Their values are filled by Bitrix24 automatically at the moment the entry is created. To record a specific interval, pass the total time via `seconds` in `POST` or `PATCH`. NB: Bitrix24 systematically sets the interval ~1 hour ahead of `createdDate` (auto-fill on its side) — this is B24 behavior; the date wrapper does not touch it.
6. **The list may show entries inaccessible to the card.** The global/per-task list (`task.elapseditem.getlist`) and the card (`task.elapseditem.get`) are different B24 methods with different access checks: the list sometimes returns entries belonging to others, for which the card returns `422 ITEM_NOT_FOUND_OR_NOT_ACCESSIBLE`. This is an access mismatch on the Bitrix24 side.
7. **There is no separate `/fields` endpoint for entries.** `GET /v1/tasks/:taskId/time/fields` returns `400 WRONG_PATH` — that path is not served. See the "Key fields" section above or `/v1/guide` for the field set.

## Typical scenario

1. Find or create a task: [`GET /v1/tasks`](./list.md) / [`POST /v1/tasks`](./create.md).
2. Add an entry: [`POST /v1/tasks/:taskId/time`](./time/create.md) with `seconds` and, if needed, `comment` and `userId`.
3. View the tracking history: [`GET /v1/tasks/:taskId/time`](./time/list.md).
4. Adjust the duration or comment: [`PATCH /v1/tasks/:taskId/time/:itemId`](./time/update.md).
5. Delete an erroneous entry: [`DELETE /v1/tasks/:taskId/time/:itemId`](./time/delete.md).

## Limits

| Limit | Value |
|-------|----------|
| Rate limit | shared across the API — see [Limits and optimization](/docs/optimization) |

## See also

- [Tasks](../tasks.md) — parent entity
- [Task comments](../task-comments.md) — another nested resource of a task
- [Employees](/docs/entities/users) — source of `USER_ID`
- [Limits and optimization](/docs/optimization) — rate limits
