Reports API
Keito exposes project-level financial reports for integrations that need to reconcile budgets and uninvoiced work without downloading raw financial records.
Both endpoints require a full-access API key bound to a user with the relevant Keito permissions. A key never grants more access than its user has:
| Bound user | Project budget report | Uninvoiced report | Project visibility |
|---|---|---|---|
| Owner or Administrator | Yes | Yes | All workspace projects |
| Manager | Requires View billable rates | Requires View billable rates and View invoices | Actively managed projects only |
| Other member | No | No | None |
| Personal read-only sync key | No | No | None |
Keito does not currently provide a workspace-wide read-only reporting credential. Use a full-access integration key created for an appropriately permissioned user, store it as a secret, and rotate or revoke it when the integration is retired.
Project Budget Report
GET /api/v2/reports/project_budget
This is the supported equivalent of Harvest’s GET /v2/reports/project_budget.
Request
curl "https://app.keito.ai/api/v2/reports/project_budget?is_active=true&per_page=2000" \
-H "Authorization: Bearer $KEITO_API_KEY" \
-H "Keito-Account-Id: $KEITO_ACCOUNT_ID"
| Parameter | Type | Required | Description |
|---|---|---|---|
is_active |
boolean | No | Return active or inactive projects only. |
page |
integer | No | Page number, starting at 1. |
per_page |
integer | No | Results per page, default and maximum 2000. |
Response
{
"results": [
{
"project_id": "clxproject123",
"project_name": "Implementation",
"client_id": "clxclient123",
"client_name": "Example Client",
"budget_is_monthly": false,
"budget_by": "project",
"is_active": true,
"budget": 20000,
"budget_spent": 5250,
"budget_remaining": 14750
}
],
"per_page": 2000,
"total_pages": 1,
"total_entries": 1,
"next_page": null,
"previous_page": null,
"page": 1,
"links": {
"first": "/api/v2/reports/project_budget?page=1&is_active=true&per_page=2000",
"next": null,
"previous": null,
"last": "/api/v2/reports/project_budget?page=1&is_active=true&per_page=2000"
}
}
budget, budget_spent, and budget_remaining use the unit selected by budget_by. For fee budgets, spend is rounded time multiplied by the historical effective project, task, or person rate for the entry’s work date. A later change to a person’s default rate does not reprice earlier work. Invoice discounts, payments, taxes, and retainer credits do not change budget consumption.
For hours budgets, the values are hours. For project-cost budgets, they are internal cost and are protected by the same elevated financial permissions.
Uninvoiced Report
GET /api/v2/reports/uninvoiced
This is the supported equivalent of Harvest’s GET /v2/reports/uninvoiced?from=...&to=....
Request
curl "https://app.keito.ai/api/v2/reports/uninvoiced?from=20260101&to=20260131&per_page=2000" \
-H "Authorization: Bearer $KEITO_API_KEY" \
-H "Keito-Account-Id: $KEITO_ACCOUNT_ID"
| Parameter | Type | Required | Description |
|---|---|---|---|
from |
string | Yes | Inclusive start date in YYYYMMDD format. |
to |
string | Yes | Inclusive end date in YYYYMMDD format. The range cannot exceed 365 days. |
include_fixed_fee |
boolean | No | Include fixed-fee projects and their remaining fee balance. Defaults to true. |
page |
integer | No | Page number, starting at 1. |
per_page |
integer | No | Projects per page, default and maximum 2000. |
Response
{
"results": [
{
"client_id": "clxclient123",
"client_name": "Example Client",
"project_id": "clxproject123",
"project_name": "Implementation",
"currency": "GBP",
"total_hours": 42.5,
"uninvoiced_hours": 12.5,
"uninvoiced_expenses": 125,
"uninvoiced_amount": 1375
}
],
"per_page": 2000,
"total_pages": 1,
"total_entries": 1,
"next_page": null,
"previous_page": null,
"page": 1,
"links": {
"first": "/api/v2/reports/uninvoiced?page=1&from=20260101&to=20260131&per_page=2000",
"next": null,
"previous": null,
"last": "/api/v2/reports/uninvoiced?page=1&from=20260101&to=20260131&per_page=2000"
}
}
Calculation Rules
- Time and expenses are filtered by their work or spent date, inclusive of
fromandto. - Only billable, unbilled work is included. Running and rejected time is excluded.
- If the workspace allows documented work before approval, unsubmitted, submitted, and approved time and expenses are included. Otherwise, only approved work is included.
- Time uses the workspace rounding rule and the historical effective billing rate for the entry’s work date.
- Expenses contribute their recorded billable cost.
- For time-and-materials projects,
uninvoiced_amountis unbilled time at those historical rates plus eligible unbilled expenses. - For fixed-fee projects, the service amount is the project fee minus non-voided service invoice lines. It becomes eligible when the report end date reaches the project’s start date, or its creation date when no start date exists. The
fromdate does not split that outstanding fixed-fee balance. - Payments, invoice discounts, taxes, retainer funding, and retainer credits do not reduce the fixed-fee balance. Set
include_fixed_fee=falseto omit fixed-fee projects.
Task or Category Hours
To reproduce cumulative hours by Harvest task/category, list time entries for the same date range and group rounded_hours by task_id (or by task.name from the embedded task object):
curl "https://app.keito.ai/api/v2/time_entries?from=2026-01-01&to=2026-01-31&per_page=2000" \
-H "Authorization: Bearer $KEITO_API_KEY" \
-H "Keito-Account-Id: $KEITO_ACCOUNT_ID"
Time-entry responses expose rounded_hours, budgeted, is_billed, and an authorized stored historical billable_rate. They do not expose a calculated billable_amount or approval_status; use the financial report endpoints for invoice-eligible monetary totals.
Capability Detection
Responses include financial-reports-v1 in the X-Keito-Features header. Treat a 403 as a credential or bound-user permission failure, not as an empty report.