---
title: "Release Time Entries"
method: POST
path: "/v1/time-entries/release"
tags: ["time-entries"]
---

# Release Time Entries

`POST /v1/time-entries/release`

## Request

- Base URL: `https://app.tokumalabs.com/api`
- URL: `https://app.tokumalabs.com/api/v1/time-entries/release`
- Auth: OAuth 2
  - Password
  - Token URL: `v1/auth/token`

Release multiple time entries.

Once released, time entries cannot be edited or deleted. This
action is irreversible. The structural validation matrix (empty,
no matter, no activity code) is shared with ``/release/check``;
rejections come back as structured ``FailedRelease`` entries the
client renders per-entry.

The ``released`` list carries the freshly-inserted
``TimeEntryReleaseSyncDto`` rows so the desktop client can apply
them to its local SQLite mirror immediately (Channel A) rather
than wait for the next cursor-sync round.

## Request body

- ReleaseTimeEntriesRequest: Request model for releasing multiple time entries.
  - `time_entry_ids` integer[], required: List of time entry IDs to release

## Response `200`

Successful Response

- ReleaseTimeEntriesResponse: Response model for bulk release operation. The ``released`` list carries the freshly-inserted release rows in the cursor-sync wire shape (``TimeEntryReleaseSyncDto``); the desktop client applies them directly to local SQLite via the same idempotent upsert used by ``GET /api/v1/sync``, so the user sees ``is_released`` flip immediately without waiting for the next sync round.
  - `released` TimeEntryReleaseSyncDto[]: Successfully created release rows in cursor-sync wire shape
    - `id` integer, required
    - `organization_id` string, required
    - `user_id` string, required
    - `time_entry_id` integer, required
    - `released_at` string, date-time, required
    - `export_id` integer, nullable
    - `created_at` string, date-time, required
    - `updated_at` string, date-time, required
    - `version` integer, required
  - `failed` FailedRelease[]: Per-entry rejections with structured codes
    - `time_entry_id` integer, required: ID of the time entry that failed to release
    - `code` 'INVALID_CREDENTIALS' | 'REFRESH_TOKEN_REQUIRED' | 'INVALID_REFRESH_TOKEN' | 'REFRESH_TOKEN_REUSE_DETECTED' | 'USER_NOT_FOUND' | 'NOT_AUTHENTICATED' | 'MS365_CONFIG_NOT_FOUND' | 'MS365_INVALID_STATE' | 'MS365_TOKEN_EXCHANGE_FAILED' | 'MS365_INVALID_USER' | 'MS365_GRAPH_REQUEST_FAILED' | 'MS365_CREDENTIALS_REJECTED' | 'MS365_PERMISSION_DENIED' | 'WRONG_AUTH_METHOD' | 'CAPABILITY_REQUIRED' | 'LAST_ADMINISTRATOR' | 'TOO_MANY_REQUESTS' | 'VALIDATION_ERROR' | 'NOT_FOUND' | 'CONFLICT' | 'ALREADY_EXISTS' | 'STALE_CURSOR' | 'INVALID_CURSOR' | 'DATA_WIPE_FORBIDDEN_ENV' | 'TIME_BLOCK_INVALID_TIME_RANGE' | 'TIME_BLOCK_INVALID_SEGMENT' | 'SEGMENT_NOT_ASSIGNABLE' | 'TIME_BLOCK_INVALID_ACTIVITY_CODE' | 'TIME_ENTRY_RELEASE_NOT_FOUND' | 'TIME_ENTRY_RELEASE_EMPTY' | 'TIME_ENTRY_RELEASE_NO_MATTER' | 'TIME_ENTRY_RELEASE_NO_ACTIVITY_CODE' | 'TIME_ENTRY_RELEASE_NARRATIVE_PENDING' | 'TIME_ENTRY_NO_BLOCKS' | 'TIME_ENTRY_MERGE_NOT_FOUND' | 'TIME_ENTRY_MERGE_RELEASED' | 'TIME_ENTRY_MERGE_DIFFERENT_DATE' | 'TIME_ENTRY_MERGE_SELF' | 'GENERATION_UNAVAILABLE' | 'GENERATION_AI_INVALID_RESPONSE' | 'GENERATION_AI_PROVIDER_ERROR' | 'GENERATION_INPUT_TOO_LARGE' | 'GENERATION_CONCURRENT' | 'GENERATION_IN_PROGRESS' | 'GENERATION_TIMEOUT_RECOVERED' | 'GENERATION_DISPATCH_FAILED' | 'GENERATION_DISPATCH_LOST' | 'GENERATION_FAILED' | 'SPAN_EVENT_DISPATCH_EXHAUSTED' | 'EXTRACTION_IMAGE_NOT_FOUND' | 'EXTRACTION_IMAGE_INVALID' | 'EXTRACTION_AI_INVALID_RESPONSE' | 'EXTRACTION_AI_PROVIDER_ERROR' | 'EXTRACTION_INPUT_TOO_LARGE' | 'REASSEMBLY_ENRICH_FAILED' | 'REASSEMBLY_REBUILD_FAILED' | 'REASSEMBLY_TIMEZONE_INVALID' | 'REASSEMBLY_FAILED' | 'SPAN_OWNERSHIP_MISMATCH' | 'SPAN_INGEST_FAILED' | 'CAPTURE_IMAGE_OWNERSHIP_MISMATCH' | 'DATABASE_UNAVAILABLE' | 'UNKNOWN_ERROR', required: Centralized error codes for the API.
    - `message` string, required: Default human-readable message paired with code

## Other responses

- `400`: Validation error or domain rule violation.
- `401`: Missing, expired, or invalid authentication.
- `403`: Authenticated but not permitted.
- `404`: Resource not found.
- `409`: Resource state conflict (duplicate, illegal transition).
- `422`: Request body failed schema validation.
- `500`: Server-side failure.

---

[API](https://skmtc.dev/tokumalabs/apis/tokuma-user-api.md) - [All operations](https://skmtc.dev/tokumalabs/apis/tokuma-user-api/llms.txt) - [OpenAPI document](https://skmtc.dev/tokumalabs/apis/tokuma-user-api/revisions/1f8201af459b?raw)
