Error Handling
Keito API v2 returns Harvest-compatible error bodies:
{
"error": "bad request",
"error_description": "Missing Keito-Account-Id header"
}
HTTP Status Codes
| Code | Meaning | Common Cause |
|---|---|---|
| 400 | Bad Request | Missing Keito-Account-Id, missing required field, invalid value |
| 401 | Unauthorized | Missing or invalid API key |
| 403 | Forbidden | User lacks permission for the company or resource |
| 404 | Not Found | Resource does not exist or is outside the company |
| 409 | Conflict | Conflict such as starting a timer while another timer is running, restarting a timer without replace_running, or deleting an approved time entry |
| 429 | Too Many Requests | Rate limit exceeded |
| 500 | Internal Server Error | Server-side error |
Retry Strategy
Do not retry validation, authorization, or conflict errors unchanged. For 429 responses, wait before retrying. For 5xx responses, use exponential backoff starting at 1 second.
Running Timer Conflict
Starting or restarting a running timer returns 409 Conflict when another timer is already active and replace_running was not supplied:
{
"error": "running_timer_conflict",
"error_description": "A timer is already running.",
"message": "A timer is already running.",
"running_entry": {
"id": "running_time_entry_id"
},
"running_entry_count": 1
}
Resolve this by showing the active timer, stopping it, or retrying the start/restart with replace_running: true when the user or integration explicitly wants to switch timers.
The Keito CLI retries transient network and server errors automatically. Client errors are returned immediately with a deterministic exit code.