# Keito — Complete Documentation > Keito is a time tracking and invoicing platform for professional services teams. It offers flat pricing with unlimited users, projects, and clients. Built by OSO, a London-based software consultancy specialising in Apache Kafka and streaming infrastructure. Keito supports the full professional services workflow: track time with timers or manual entry, manage projects with budgets and tasks, log expenses with receipt attachments, generate and send invoices, and sync with Xero and QuickBooks for accounting. It uses a role-based permission system (Member, Manager, Admin) and includes timesheet approval workflows. Keito treats AI agents as first-class team members. Agent entries are tracked via the REST API, Node SDK (@keito/sdk), Python SDK (keito-python), or CLI. Every time entry and expense has a `source` field (web, cli, api, agent) and an optional `metadata` JSON object for agent context (agent_id, session_id, model, token counts). Agent users have their own rates, project assignments, and approval workflows. LLM token costs flow through the expense pipeline via the built-in "LLM Usage" category. Plans: Free (basic tracking), Pro (invoicing, expenses, budgets, approvals), Business (Xero/QuickBooks integration, custom branding, priority support). --- ## Setting Up Your Account source: https://keito.ai/docs/getting-started/setting-up-your-account # Setting Up Your Account Get your Keito workspace ready in just a few steps. ## Creating Your Workspace When you sign up for Keito, you'll be guided through a short onboarding wizard: 1. **Company details** — Enter your company name and set up your workspace subdomain. 2. **Choose your plan** — Select a subscription plan or start with a free trial. 3. **Preferences** — Keito auto-detects your timezone and currency based on your location. You can adjust these at any time. Once complete, you'll land on the Time Tracking page, ready to start logging hours. ## Configuring Your Workspace After setup, head to **Settings** to fine-tune your workspace: ### Time & Date Preferences - **Timezone** — Set the default timezone for your team. - **Week start day** — Choose which day your work week begins (Sunday through Saturday). - **Time format** — 12-hour or 24-hour clock. - **Date format** — Choose your preferred date display format. - **Time display** — Decimal hours (2.5h) or hours and minutes (2:30). ### Currency & Formatting - **Currency** — Set your base currency for invoicing and rates. - **Number formatting** — Configure decimal and thousands separators. ### Time Tracking Defaults - **Default entry mode** — Timer or manual entry. - **Weekly capacity** — Set the default work week hours for your team. - **Require notes** — Optionally enforce notes on every time entry. - **Entry locking** — Automatically lock entries after a set number of days. - **Rounding** — Round time entries to the nearest 5, 6, 10, 15, or 30 minutes, or leave rounding off. ### Feature Toggles Enable or disable modules based on your needs: - Expenses - Invoicing - Approvals ## Next Steps - [Invite your team](/docs/getting-started/inviting-your-team) - [Log your first time entry](/docs/getting-started/your-first-time-entry) --- ## Inviting Your Team source: https://keito.ai/docs/getting-started/inviting-your-team # Inviting Your Team Add team members to your Keito workspace so everyone can track time and collaborate on projects. ## Sending Invitations 1. Navigate to **Team** from the main menu. 2. Click **Invite Members**. 3. Enter the email addresses of the people you'd like to invite. 4. For each person, you can optionally pre-fill: - **Name** — First and last name - **Role** — Member, Manager, or Administrator - **Hourly rate** — Their default billing rate - **Weekly capacity** — Hours per week they're expected to work - **Employee or Contractor** — Their engagement type 5. Click **Send Invitations**. Invitees receive an email with a link to create their account and join your workspace. ## Bulk Invitations Need to invite many people at once? You can add multiple email addresses and configure each person's details before sending all invitations in one batch. ## What Happens Next When a team member accepts their invitation: 1. They create their Keito account via the setup link. 2. They're automatically added to your workspace with the role you assigned. 3. They can immediately start tracking time on any projects they're assigned to. ## Invitation Status Track the status of your invitations from the Team page: - **Pending** — Invitation sent, not yet accepted - **Active** — Member has joined and is using the workspace - **Archived** — Member has been deactivated ## Next Steps - [Create your first time entry](/docs/getting-started/your-first-time-entry) - [Set up projects](/docs/projects/creating-projects) for your team to track against - [Configure roles and permissions](/docs/team-management/roles-permissions) --- ## Your First Time Entry source: https://keito.ai/docs/getting-started/your-first-time-entry # Your First Time Entry Start tracking time in Keito — it only takes a few seconds. ## Before You Begin To log time, you'll need at least one project set up. If you haven't created any yet, head to [Creating Projects](/docs/projects/creating-projects) first. ## Using the Timer The fastest way to track time is with the built-in timer: 1. Go to the **Time** page. 2. Select a **Project** and **Task** from the dropdowns. 3. Optionally add a **note** describing what you're working on. 4. Click the **Start Timer** button. 5. Work on your task — the timer runs in the background and persists even if you close the browser. 6. When you're done, click **Stop** to save the entry. Only one timer can run at a time. Starting a new timer will stop the current one. ## Manual Entry Prefer to log time after the fact? Use manual entry: 1. Go to the **Time** page. 2. Select your **Project** and **Task**. 3. Enter the duration: - **Decimal format:** Type `2.5` for two and a half hours. - **Hours:Minutes format:** Type `2:30` for the same duration. 4. Add a note if needed. 5. Click **Save**. ## Start/End Time Mode For precise time logging, switch to Start/End time mode: 1. Enter your **start time** and **end time**. 2. Keito automatically calculates the duration. 3. Cross-midnight entries are supported (e.g., 11:00 PM to 1:00 AM). ## Billable vs Non-Billable Each entry can be marked as billable or non-billable. By default, entries inherit the billable setting from the task, but you can override this per entry. Billable entries contribute to invoiceable amounts on reports and invoices. ## Editing and Deleting Entries - Click on any entry to edit its details. - Use the delete option to remove an entry entirely. - Entries that have been approved or invoiced are locked and cannot be edited. ## Day View vs Week View Keito offers two ways to view your time: - **Day view** — See all entries for a single day with a running total. - **Week view** — A grid showing hours per project and task across the entire week. Great for filling in time at the end of the week. Learn more about the [Week View](/docs/time-tracking/week-view). ## Next Steps - [Learn about timers vs manual entry](/docs/time-tracking/timers-vs-manual) - [Submit your timesheet for approval](/docs/time-tracking/submitting-timesheets) - [Log an expense](/docs/expenses/logging-expenses) --- ## Understanding Agent Entries source: https://keito.ai/docs/getting-started/agent-entries # Understanding Agent Entries Keito now supports AI agents as first-class team members. When an agent tracks time or logs expenses through the API or CLI, those entries appear in your workspace alongside human entries — with clear visual indicators. ## What Are Agent Entries? Agent entries are time entries and expenses created by AI agents (like Claude Code, Codex, or custom bots) through the Keito API or CLI. They flow through the same pipeline as human entries: projects, tasks, rates, approvals, invoices, and reports. ## How to Spot Them - **Violet "Agent" badge** — appears on time entries and expenses created by agents in the day view, week view, and expenses list. - **Amber "CLI" badge** — appears on entries created via the Keito CLI. - **No badge** — entries created through the web UI (the default). ## Filtering by Source In any list view, use the source filter to show only entries from a specific source: - **All** — everything (default) - **Web** — entries from the web UI - **CLI** — entries from the command-line tool - **API** — entries from direct API calls - **Agent** — entries from AI agents ## What This Means for Your Workflow - **Approvals**: Agent entries go through the same approval workflow. Managers review and approve them like human timesheets. - **Invoicing**: Agent work appears on invoices with clear labelling. Clients see what was done by humans and what was done by agents. - **Reports**: Filter reports by source to see agent hours vs. human hours, LLM costs by project, and agent productivity metrics. ## Next Steps - [Managing Agent Users](/docs/team-management/agent-users) — set up agent profiles with rates and project assignments. - [LLM Usage Expenses](/docs/expenses/llm-usage) — understand how token costs are tracked. - **For developers**: [Developer Overview](/docs/developer/introduction) — start integrating agents with the Keito API. --- ## Tracking Time source: https://keito.ai/docs/time-tracking/tracking-time # Tracking Time Keito provides flexible options for recording how your team spends their hours. Whether you prefer real-time timers or logging time after the fact, everything ends up in the same place — organised by project and task. ## How Time Entries Work Every time entry in Keito includes: - **Date** — When the work was performed - **Project** — Which project the work belongs to - **Task** — The specific type of work (e.g., Design, Development, Meetings) - **Duration** — How long you worked - **Notes** — Optional context about what you did - **Billable status** — Whether the time is chargeable to the client ## Entry Methods ### Timer Click start, work, click stop. The timer runs in the background and persists across page refreshes and browser sessions. You can only run one timer at a time. ### Duration Entry Type the hours directly: - **Decimal:** `1.5` = 1 hour 30 minutes - **Hours:Minutes:** `1:30` = 1 hour 30 minutes ### Start/End Time Enter a start and end time, and Keito calculates the duration. Supports entries that cross midnight. ## Quick Actions - **Copy** — Duplicate an entry to quickly log similar work. - **Restart** — Start a new timer based on an existing entry's project and task. - **Edit** — Modify any unlocked entry's details. - **Delete** — Remove an entry entirely. ## Entry Locking Entries become locked (read-only) when they are: - **Approved** — After a manager approves a submitted timesheet - **Invoiced** — After the time has been included on an invoice - **Auto-locked** — If your workspace has entry locking enabled after a set number of days Locked entries cannot be edited or deleted without administrator intervention. ## Time Rounding If enabled by your administrator, entries can be automatically rounded to the nearest increment: - 5 minutes - 6 minutes (1/10 hour) - 10 minutes - 15 minutes (1/4 hour) - 30 minutes Rounding applies when the entry is saved. ## Daily and Weekly Totals The time page shows your running totals: - **Daily total** — Sum of all entries for the selected date - **Weekly total** — Sum of all entries for the current week These help you stay on track with your weekly capacity target. ## Filtering Entries Administrators and managers can filter the time view by: - Team member - Client - Project - Task - Billable status - Approval status - Date range --- ## Timers vs Manual Entry source: https://keito.ai/docs/time-tracking/timers-vs-manual # Timers vs Manual Entry Keito supports two primary approaches to time tracking. Choose the one that fits your workflow, or mix and match as needed. ## Timer Mode Best for: Tracking time as you work, especially when switching between tasks frequently. **How it works:** 1. Select a project and task. 2. Click the start button to begin the timer. 3. Work on your task — the timer counts in the background. 4. Click stop when you're done. **Key behaviors:** - Only one timer can run at a time across your account. - Starting a new timer automatically stops the previous one and saves it. - The timer persists across page refreshes, tab switches, and browser closures. - A visual indicator shows when a timer is running. - You can edit the entry after stopping if needed. **Best practices:** - Start the timer when you begin a task, even if you forget to stop it right away — you can adjust the duration later. - Use the restart action on previous entries to quickly start tracking similar work. ## Manual Entry Mode Best for: Logging time after the fact, filling in timesheets at the end of the day or week. **How it works:** 1. Select a project, task, and date. 2. Enter the duration in decimal (e.g., `2.5`) or hours:minutes (e.g., `2:30`) format. 3. Save the entry. **Key behaviors:** - No running timer — you simply record how long you worked. - Enter time for any date (past or present, subject to locking rules). - Ideal for people who prefer to batch their time logging. **Best practices:** - Use the [Week View](/docs/time-tracking/week-view) to quickly fill in an entire week at once. - Keep brief notes so you remember what you worked on when logging later. ## Start/End Time Mode A variant of manual entry where you specify when you started and stopped: - Enter a start time and end time. - Keito calculates the duration automatically. - Supports entries that cross midnight (e.g., 11:00 PM to 1:30 AM). This mode is useful when you need precise time records or when your work schedule doesn't follow standard hours. ## Switching Between Modes Your administrator can set a default entry mode for the workspace, but individual users can switch between timer and manual mode at any time from the time entry form. ## Which Should I Use? | Scenario | Recommended Mode | |----------|-----------------| | Active task switching throughout the day | Timer | | Filling in time at end of day/week | Manual | | Precise start/end records required | Start/End Time | | Collaborative sessions with defined schedules | Start/End Time | | Quick one-off entries | Manual | --- ## Week View source: https://keito.ai/docs/time-tracking/week-view # Week View The Week View gives you a bird's-eye perspective of your time across an entire week, organised by project and task. ## Overview Week View displays a grid with: - **Rows** — One row per unique project/task combination you've tracked time against - **Columns** — One column per day of the week (Monday through Sunday, or your configured week start) - **Cells** — The hours logged for that project/task on that day ## Using Week View ### Viewing Time Navigate to the **Time** page and toggle to **Week View**. You'll see all your entries for the current week summarized in the grid. - Each cell shows the total hours for that project/task/day combination. - Row totals show the weekly hours per project/task. - Column totals show your daily totals. - The overall total shows your entire week's hours. ### Entering Time Click on any cell in the grid to enter or modify hours: 1. Click an empty cell to add a new entry. 2. Type the duration (decimal or HH:MM format). 3. Press Enter or Tab to move to the next cell. This makes it fast to fill in an entire week of time in just a few seconds. ### Navigating Weeks Use the date navigation arrows to move between weeks, or click the date to open a calendar picker and jump to any specific week. ## When to Use Week View Week View is particularly useful when: - You log time at the end of the week and need to fill in multiple days at once. - You want to see how your hours are distributed across projects. - You need a quick overview of whether you've hit your weekly capacity. - You track time against the same projects and tasks each day. ## Day View vs Week View | Feature | Day View | Week View | |---------|----------|-----------| | Entry detail | Full details per entry (notes, billable, times) | Hours only per cell | | Multiple entries per task/day | Yes, each shown separately | Aggregated into one cell | | Timer controls | Yes | No | | Best for | Detailed daily tracking | Weekly overview and batch entry | | Notes | Visible inline | Added when clicking into a cell | --- ## Submitting Timesheets source: https://keito.ai/docs/time-tracking/submitting-timesheets # Submitting Timesheets When the Approvals feature is enabled, team members submit their time entries for manager review before they're considered final. ## How Submission Works 1. Track your time throughout the week as normal. 2. When your timesheet is complete, click **Submit for Approval**. 3. All unsubmitted entries for the selected period are bundled and sent to your manager. 4. You'll be notified when your timesheet is approved or rejected. ## Submission Scope When you submit, all of your **unsubmitted** time entries within the current period are included. You don't need to select individual entries — the submission covers everything that hasn't been submitted yet. ## What Happens After Submission Once submitted, your entries move to the **Submitted** state: - You can still view your submitted entries but cannot edit them. - Your manager receives a notification to review your timesheet. - The entries remain in Submitted state until your manager takes action. ## Approval Outcomes Your manager will either: - **Approve** — Entries are locked and can be included in invoices and reports. - **Reject** — Entries return to an editable state. You'll receive a notification explaining why, and can make corrections before resubmitting. ## Reminders If your administrator has configured timesheet reminders, you'll receive email notifications: - **Reminder** — A nudge to submit before the deadline (e.g., Friday at 9 AM). - **Deadline** — A final reminder when the submission window is closing (e.g., Friday at 5 PM). ## Tips - Submit regularly (daily or weekly) rather than letting entries accumulate. - Add notes to your entries so your manager has context when reviewing. - If you realize you missed time after submitting, log additional entries and submit again — your manager will see the new batch separately. ## Without Approvals If the Approvals feature is not enabled in your workspace, time entries are considered final as soon as they're saved. There's no submission step required. --- ## Approving Time source: https://keito.ai/docs/time-tracking/approving-time # Approving Time Managers and administrators can review and approve submitted timesheets from their team. ## Accessing the Approval Queue Navigate to **Time > Approval** to see all pending timesheet submissions from your team. The queue shows: - Team member name - Period covered - Total hours submitted - Breakdown of billable vs non-billable hours ## Reviewing a Submission Click on a team member's submission to see the full details: - Individual time entries with projects, tasks, and notes - Daily totals for the period - Any entries that look unusual (e.g., overtime, missing notes) ## Taking Action For each submission, you can: ### Approve Click **Approve** to accept the timesheet. This: - Locks the entries from further editing - Makes the time available for invoicing - Notifies the team member that their timesheet was approved ### Reject Click **Reject** to send the timesheet back for corrections. You can include a reason for the rejection. This: - Returns entries to an editable state - Notifies the team member with your feedback - The member can then correct and resubmit ## Bulk Actions When multiple team members have pending submissions, you can approve or reject several at once: 1. Select the submissions you want to act on. 2. Choose **Approve Selected** or **Reject Selected**. 3. For bulk rejections, you can add a shared note. ## Who Can Approve - **Managers** can approve timesheets from their assigned teammates only. - **Administrators** can approve any timesheet in the workspace. The teammate assignment is configured under [Team Management](/docs/team-management/managing-members). ## Withdrawing Approvals If a manager has the "Withdraw Approvals" permission, they can undo a previous approval: - The entries return to Submitted state. - The team member is notified. - This is useful when an error is discovered after approval. ## Approval and Invoicing Approved time entries become available for inclusion on invoices. Until time is approved, it won't appear in the invoicing workflow (when approvals are enabled). --- ## Agent & CLI Time Entries source: https://keito.ai/docs/time-tracking/agent-cli-entries # Agent & CLI Time Entries Time entries created by agents and the CLI work exactly like manual entries — with two additional fields that provide context about their origin. ## The source Field Every time entry has a `source` field indicating how it was created: - `web` — created in the Keito web app - `cli` — created via the Keito CLI - `api` — created via a direct API call - `agent` — created by an AI agent This field is set automatically. You cannot change it after creation. ## The metadata Field Agent and API entries can include a `metadata` JSON object with contextual information: | Field | Description | Example | |---|---|---| | `agent_id` | Identifier for the agent | `review-bot-01` | | `agent_type` | Agent platform | `claude-code` | | `session_id` | Unique session identifier | `550e8400-e29b-...` | | `model` | LLM model used | `claude-opus-4-6` | Metadata is visible in the entry detail view and can be used for filtering in reports. ## Visual Indicators Agent entries display a violet "Agent" badge. CLI entries display an amber "CLI" badge. These badges appear in: - Day view - Week view - Expenses list - Entry detail view ## Approving Agent Time Agent entries follow the same approval workflow as human entries. When reviewing timesheets, you can: 1. Filter by source to review only agent entries. 2. Check the metadata to understand what the agent did. 3. Approve, reject, or edit as needed. ## Related - [Tracking Time](/docs/time-tracking/tracking-time) — the basics of time entries - [Approving Time](/docs/time-tracking/approving-time) — the approval workflow - [Developer Overview](/docs/developer/introduction) — for building agent integrations --- ## Logging Expenses source: https://keito.ai/docs/expenses/logging-expenses # Logging Expenses Track project-related expenses alongside your time to keep a complete picture of project costs. ## Creating an Expense 1. Navigate to the **Expenses** page. 2. Click **New Expense**. 3. Fill in the details: - **Date** — When the expense was incurred - **Project** — Which project to charge it to - **Category** — The type of expense (e.g., Meals, Travel, Software) - **Amount** — The total cost - **Notes** — Description or context for the expense - **Billable** — Whether this expense should be billed to the client 4. Optionally attach a receipt (see [Receipts & Attachments](/docs/expenses/receipts)). 5. Click **Save**. ## Standard vs Unit-Based Categories Keito supports two types of expense categories: ### Standard Categories Enter a fixed dollar amount for the expense. Examples: meals, software subscriptions, office supplies. ### Unit-Based Categories Enter a quantity and the total is calculated automatically based on a per-unit rate. Examples: - **Mileage** — Enter miles driven, calculated at $0.67/mile - **Printing** — Enter pages printed, calculated at a per-page rate The unit rate is configured by your administrator in [Expense Categories](/docs/expenses/expense-categories). ## Billable Expenses Each expense can be marked as billable or non-billable: - **Billable** — The expense will appear on client invoices and in billable reports. - **Non-billable** — The expense is tracked internally but not charged to clients. By default, expenses inherit the billable setting from their project, but you can override this per expense. ## Editing and Deleting - Click on any expense to edit its details. - Use the delete option to remove an expense. - Expenses that have been approved or invoiced are locked and cannot be modified. ## Expense Approval When the Approvals feature is enabled, expenses follow the same workflow as time entries: 1. Log your expenses. 2. Submit them for approval. 3. Your manager reviews and approves or rejects. 4. Approved expenses are locked and available for invoicing. ## Filtering Filter your expense list by: - Date range - Project - Client - Category - Billable status - Approval status Managers and administrators can also filter by team member to see expenses across the team. --- ## Receipts & Attachments source: https://keito.ai/docs/expenses/receipts # Receipts & Attachments Attach receipt images or documents to your expenses for record-keeping and client billing. ## Supported Formats Keito accepts the following file types for receipts: - **Images:** PNG, JPG, JPEG, GIF - **Documents:** PDF Maximum file size: **10 MB** per receipt. ## Attaching a Receipt ### Drag & Drop Drag a file from your desktop directly onto the expense form's receipt area. ### File Upload Click the upload area on the expense form and select a file from your device. ### Mobile Camera On mobile devices, the upload button opens your camera, letting you snap a photo of a receipt directly. ## One Receipt Per Expense Each expense supports a single receipt attachment. If you need to replace an existing receipt: 1. Open the expense for editing. 2. Remove the current receipt. 3. Upload the new one. If you have multiple receipts for a single expense (e.g., a meal with separate drink and food receipts), consider logging them as separate expenses or combining the images before uploading. ## Receipt Storage Receipts are stored securely in the cloud and are accessible to: - The person who logged the expense - Managers reviewing the expense for approval - Administrators ## Receipts on Invoices When creating an invoice from tracked expenses, receipt information can be included as supporting documentation. This helps clients understand what they're being billed for. ## Tips - Upload receipts at the time of the expense so you don't lose them. - Use your phone's camera feature for quick capture while on the go. - Make sure the receipt image is clear and legible before uploading. --- ## Expense Categories source: https://keito.ai/docs/expenses/expense-categories # Expense Categories Expense categories help you organise and classify different types of expenses across your projects. ## Default Categories Keito comes with common expense categories pre-configured: - Meals & Entertainment - Transportation - Lodging - Mileage (unit-based, default $0.67/mile) - Software & Subscriptions - Office Supplies Your administrator can customise these to match your organisation's needs. ## Category Types ### Standard Categories A simple category where users enter a fixed dollar amount. Use this for most expense types like meals, travel, supplies. ### Unit-Based Categories A category with a per-unit rate. Users enter a quantity and the total is calculated automatically: - **Unit name** — What's being measured (e.g., "miles", "pages", "units") - **Unit price** — The rate per unit (e.g., $0.67 per mile) When logging an expense with a unit-based category, the user enters the quantity (e.g., 45 miles) and the total ($30.15) is calculated automatically. ## Managing Categories Administrators can manage expense categories from **Manage > Expense Categories**: ### Creating a Category 1. Click **New Category**. 2. Enter the category name. 3. Choose Standard or Unit-based. 4. For unit-based categories, enter the unit name and price. 5. Save. ### Editing a Category Click on any category to update its name, type, or unit pricing. Changes apply to new expenses going forward — existing expenses retain their original values. ### Archiving a Category Categories that are no longer needed can be archived: - Archived categories won't appear in the dropdown when logging new expenses. - Existing expenses with that category remain unchanged. - Archived categories can be restored at any time. ## Billable Default Each category can have a default billable setting. When a user selects that category, the billable toggle is pre-set accordingly. Users can still override this per expense. --- ## LLM Usage Expenses source: https://keito.ai/docs/expenses/llm-usage # LLM Usage Expenses Keito includes a built-in "LLM Usage" expense category for tracking the cost of AI model usage. This category is automatically created in every workspace. ## How It Works LLM usage expenses use the unit-based expense model: quantity (tokens in thousands) × unit_price = total_cost For example: 45k tokens at £0.003/k = £0.135. The expense flows through the standard pipeline: it's allocated to a project, included in reports, and can be invoiced to clients. ## When LLM Expenses Are Created - **Automatically** — when an agent integration logs token usage via the API or CLI. - **Manually** — you can create LLM usage expenses in the web UI like any other expense. ## What's Captured | Field | Description | |---|---| | `project_id` | The project the tokens were used for | | `quantity` | Token count (typically in thousands) | | `unit_price` | Cost per unit | | `total_cost` | Calculated total | | `notes` | Model name, context (e.g., "claude-opus-4-6: 25k input + 15k output") | | `metadata` | Structured token breakdown, session ID | ## Viewing LLM Costs 1. Go to **Expenses** and filter by category → "LLM Usage". 2. Use the source filter to see only agent-created expenses. 3. In **Reports → Expense Reports**, group by category to see total LLM spend across projects. ## Invoicing LLM Costs LLM expenses can be included on client invoices as a separate line item or grouped with other project expenses. See [Invoicing Agent Work](/docs/invoicing/agent-work) for details. --- ## Creating Projects source: https://keito.ai/docs/projects/creating-projects # Creating Projects Projects are the core organising unit in Keito. Every time entry and expense is associated with a project, which belongs to a client. ## Creating a New Project 1. Navigate to **Projects** and click **New Project**. 2. Fill in the required fields: - **Project name** — A clear, descriptive name - **Client** — The client this project belongs to (required) 3. Configure optional details: - **Project code** — A short identifier (e.g., "ACME-WEB") - **Start date** and **End date** — The project timeline - **Notes** — Internal notes about the project ## Project Types Choose the billing model that matches your client agreement: ### Time & Materials Bill the client based on actual hours worked at agreed rates. This is the most common project type for professional services. - Hours are tracked and billed at the configured rate. - The client pays for actual time spent. ### Fixed Fee A predetermined total fee for the project, regardless of hours worked. - You still track time for internal purposes and profitability analysis. - The fixed fee is the total invoiceable amount. ### Non-Billable Internal projects where no time or expenses are billed to a client. - Useful for internal meetings, training, admin time, or R&D. - Hours still count toward capacity and utilization metrics. ## Billing Configuration For billable projects, configure how rates are applied: ### Bill By Options | Option | Description | |--------|-------------| | Project rate | A single hourly rate for all work on this project | | Task rates | Different rates for different types of work (e.g., Design vs Development) | | Per-person rates | Different rates for each team member | | Non-billable | No billing on this project | ### Setting Rates - **Project rate** — Enter one rate that applies to all entries. - **Task rates** — Set a rate for each task assigned to the project. - **Per-person rates** — Set a rate for each team member, or use their default rate. ## Project Lifecycle ### Active Projects New projects start in an Active state. Team members can track time and expenses against active projects. ### Archiving Projects When a project is complete: 1. Open the project and click **Archive**. 2. The project no longer appears in active project lists or time entry dropdowns. 3. All historical data (entries, invoices, reports) is preserved. 4. Archived projects can be restored at any time. ## Next Steps - [Set up budgets](/docs/projects/budgets) to track spending - [Assign team members](/docs/projects/project-team) to the project - [Add tasks](/docs/projects/tasks) for work categorization --- ## Budgets & Tracking source: https://keito.ai/docs/projects/budgets # Budgets & Tracking Set budgets on your projects to monitor spending and get alerted before costs exceed expectations. ## Budget Types Choose the budget type that matches how you want to track spending: | Budget Type | Tracks | Best For | |-------------|--------|----------| | Total Hours | Total hours worked across the project | Time-capped engagements | | Total Fees | Total billable amount (hours × rates) | Fixed-budget projects | | Per-Task | Individual budget per task | Phased or scoped work | | Per-Person | Individual budget per team member | Resource-capped projects | | None | No budget tracking | Open-ended work | ## Setting a Budget 1. Open the project and go to the budget section. 2. Select your budget type. 3. Enter the budget amount (hours or currency depending on type). 4. Optionally enable **monthly reset** to have the budget renew each month. ## Monthly Reset When enabled, the budget resets at the beginning of each month. This is useful for: - Retainer clients with a set monthly hour allocation - Ongoing support contracts - Monthly resource budgets Without monthly reset, the budget applies to the entire life of the project. ## Over-Budget Notifications Configure an alert threshold to get notified before a project goes over budget: - **Default threshold:** 80% of budget consumed - **Customizable:** Set any percentage that works for you - **Notification recipients:** Project managers and administrators When the threshold is crossed, email notifications are sent so you can take action. ## Including Expenses For fee-based budgets, you can optionally include expenses in the budget calculation: - **Enabled:** Both billable hours and billable expenses count toward the budget. - **Disabled:** Only billable hours are tracked against the budget. ## Budget Visibility Control who can see budget information: - **Visible to team** — All project members can see budget progress. - **Hidden from team** — Only managers and administrators can see budget details. This is configured per project. ## Monitoring Budget Progress Track budget consumption from the project detail page: - **Budget total** — The full budget amount - **Spent** — How much has been used (hours or fees) - **Remaining** — What's left - **Percentage used** — Visual progress indicator You can also see budget status across all projects from the [Budget Report](/docs/reports/budget-reports). --- ## Project Team source: https://keito.ai/docs/projects/project-team # Project Team Assign team members to projects to control who can track time and how they're billed. ## Assigning Members 1. Open the project and navigate to the **Team** section. 2. Click **Add Member**. 3. Select team members from your workspace roster. 4. Configure per-member settings if needed. ## Per-Member Settings For each team member on a project, you can configure: ### Hourly Rate - **Use default rate** — Uses the member's default hourly rate from their profile. - **Custom rate** — Set a project-specific rate for this person. This setting is only relevant when the project is set to bill by per-person rates. ### Per-Person Budget When using Per-Person budget type, set an individual hour or fee budget for each member. ## Project Managers Designate one or more team members as project managers: - Project managers receive over-budget notifications. - They can approve timesheets from team members assigned to their projects. - They may have additional permissions depending on their workspace role. ## Who Can Track Time Only assigned team members can log time and expenses against a project. If someone isn't assigned: - The project won't appear in their time entry dropdown. - They cannot create entries for that project. Administrators can always access all projects regardless of assignment. ## Removing Members To remove a team member from a project: 1. Open the project's Team section. 2. Click remove next to the member's name. Their existing time entries remain on the project — only future tracking is prevented. ## Viewing Assignments From the project detail page, you can see: - All assigned members - Their roles (manager or member) - Their hourly rates (if you have permission to view rates) - Hours tracked per person --- ## Tasks source: https://keito.ai/docs/projects/tasks # Tasks Tasks categorise the type of work being done on a project. They appear in the task dropdown when logging time entries. ## How Tasks Work Tasks represent activities like Design, Development, Testing, Project Management, or Meetings. They help you understand not just how much time was spent, but what kind of work was done. Each time entry requires both a project and a task, giving you two dimensions of categorization. ## Company-Level Tasks Tasks are defined at the company level in **Manage > Tasks**. This provides a consistent vocabulary across all projects. ### Creating Tasks 1. Go to **Manage > Tasks**. 2. Click **New Task**. 3. Enter the task name. 4. Configure defaults: - **Billable by default** — Whether time tracked to this task is typically billable. - **Default hourly rate** — An optional default rate for this type of work. ### Default Tasks Mark tasks as "default" to have them automatically added to every new project. This saves time when creating projects that use your standard set of activities. ## Project-Level Task Configuration When you assign tasks to a project, you can override company-level defaults: ### Assigning Tasks to Projects 1. Open the project settings. 2. Go to the **Tasks** section. 3. Select which tasks are available for this project. Only assigned tasks appear in the dropdown when team members log time against this project. ### Per-Project Overrides For each task on a project, you can customise: - **Billable** — Override whether this task is billable on this specific project. - **Hourly rate** — Set a task-specific rate (when billing by task rates). - **Budget** — Set a per-task budget (when using per-task budgets). ## Archiving Tasks Tasks that are no longer used can be archived: - Archived tasks don't appear in the task dropdown for new entries. - Existing entries with that task are preserved. - Archived tasks can be restored from the archive view. ## Best Practices - Keep your task list focused — too many tasks creates decision fatigue. - Use tasks for the *type* of work, not specific deliverables (use notes for that). - Common task lists: Design, Development, Testing, Project Management, Meetings, Research, Documentation. - Set billable defaults accurately to reduce manual toggling. --- ## Creating Invoices source: https://keito.ai/docs/invoicing/creating-invoices # Creating Invoices Generate professional invoices from tracked time and expenses, or create free-form invoices with manual line items. ## Invoice Types ### From Time & Expenses Create invoices pre-populated with billable hours and expenses from your projects: 1. Go to **Invoices** and click **New Invoice**. 2. Select a **Client**. 3. Choose **Import from Time & Expenses**. 4. Select the project(s) and date range to include. 5. Choose how to group line items: - **By Project** — One line item per project - **By Task** — Grouped by task across projects - **By Person** — Grouped by team member - **Detailed** — Individual time entries as separate line items 6. Review and adjust the generated line items. 7. Save as draft or send. When you create an invoice from tracked time, those entries are marked as "billed" and won't appear in future import selections. ### Free-Form Invoices Create invoices with custom line items unrelated to tracked time: 1. Go to **Invoices** and click **New Invoice**. 2. Select a **Client**. 3. Add line items manually with descriptions, quantities, and unit prices. 4. Save or send. ## Invoice Details Every invoice includes: - **Invoice number** — Auto-generated based on your configured pattern (e.g., INV-2024-0001) - **Client** — Who's being billed - **Issue date** — When the invoice was created - **Due date** — When payment is expected (based on payment terms) - **Payment terms** — Upon Receipt, Net 15, Net 30, Net 45, Net 60, or Custom - **Subject** — Optional title or reference - **Purchase order** — Optional PO number for client reference ## Line Items Each line item on an invoice has: - **Type** — Service, Product, or Expense - **Description** — What's being billed - **Quantity** — Number of units (e.g., hours) - **Unit price** — Rate per unit - **Amount** — Automatically calculated (quantity × unit price) - **Tax flags** — Whether each tax rate applies to this item - **Project** — Optional project association ## Taxes and Discounts Configure up to two tax rates and an optional discount: - **Tax 1** — First tax rate (e.g., GST at 10%) - **Tax 2** — Second tax rate (e.g., State tax at 5%) - **Discount** — Percentage off the subtotal Each line item can individually opt in or out of each tax. ## Invoice States | State | Description | |-------|-------------| | Draft | Work in progress, not yet sent to client | | Open | Sent to client, awaiting payment | | Paid | Fully paid | | Closed | Written off or cancelled | ## Saving and Editing - **Draft invoices** can be freely edited, reordered, and adjusted. - **Open invoices** can still be edited, but changes may confuse clients who've already received the original. - **Paid and Closed invoices** are locked from editing. ## Next Steps - [Send your invoice to the client](/docs/invoicing/sending-invoices) - [Record payments](/docs/invoicing/payments) - [Configure invoice settings](/docs/invoicing/invoice-settings) --- ## Sending Invoices source: https://keito.ai/docs/invoicing/sending-invoices # Sending Invoices Deliver invoices to your clients via email directly from Keito. ## Sending an Invoice 1. Open a draft or open invoice. 2. Click **Send Invoice**. 3. Configure the email: - **To** — Client's email address (pre-filled from client contact) - **Subject** — Email subject line (uses your template or custom text) - **Body** — The email message (uses your template or custom text) - **CC sender** — Optionally send a copy to yourself - **Attach PDF** — Include the invoice as a PDF attachment 4. Click **Send**. The invoice state changes from Draft to Open when first sent. ## Email Templates Keito provides customizable templates for invoice communications: ### Invoice Sent The initial email when sending an invoice to a client. Customise the subject, body text, and footer in [Invoice Settings](/docs/invoicing/invoice-settings). ### Payment Reminder A follow-up email for overdue invoices. Send reminders manually when payment is late. ### Thank You An optional message to send after receiving payment. Shows appreciation and confirms the payment was recorded. ## Customizing Messages When sending, you can: - Use the default template as-is for consistency. - Edit the subject and body for a specific invoice before sending. - Include dynamic fields that auto-fill with invoice details. ## Resending Invoices Need to resend an invoice? Open the invoice and click **Send** again. You can adjust the message before resending — useful for adding a personal note or reminder. ## Client Invoice Portal Each invoice has a unique URL that clients can access to view their invoice online. This link is included in the email and allows clients to: - View the full invoice details - See the amount due - Access online payment options (if configured) ## Tips - Set up your email templates once in Settings, then sending becomes a one-click process. - Use the CC option to keep a copy in your inbox for your records. - Send invoices promptly — the sooner you invoice, the sooner you get paid. --- ## Tracking Payments source: https://keito.ai/docs/invoicing/payments # Tracking Payments Record payments against invoices to track what's been paid and what's outstanding. ## Recording a Payment 1. Open an invoice with an outstanding balance. 2. Click **Record Payment**. 3. Enter the payment details: - **Amount** — How much was received - **Date** — When the payment was received - **Notes** — Optional reference (e.g., check number, transfer reference) - **Transaction ID** — Optional identifier for reconciliation 4. Save the payment. If the payment covers the full outstanding amount, the invoice automatically moves to **Paid** status. ## Partial Payments Keito supports partial payments: - Record any amount less than the outstanding balance. - The invoice shows the remaining due amount. - Record additional payments as they come in. - The invoice moves to Paid only when the full amount is received. ## Online Payments If you've configured a payment gateway, clients can pay directly from their invoice: ### Stripe Connect your Stripe account to accept credit card and bank transfer payments. When a client pays online: - The payment is automatically recorded in Keito. - The invoice status updates to Paid. - Transaction IDs are stored for reconciliation. ### PayPal Connect PayPal to accept payments through the client's invoice portal. Configure payment gateways in [Invoice Settings](/docs/invoicing/invoice-settings). ## Payment History Each invoice shows a complete payment history: - Payment date - Amount received - Notes and transaction IDs - Running balance ## Closing Invoices If an invoice won't be paid (e.g., written off, disputed, or cancelled), you can close it: - The invoice moves to **Closed** status. - The outstanding amount is no longer tracked as receivable. - Closed invoices are excluded from unpaid totals in reports. ## Invoice Aging From the Invoices list, you can see at a glance: - Which invoices are overdue - Days since the invoice was issued - Outstanding balances per client - Total receivables --- ## Invoice Settings source: https://keito.ai/docs/invoicing/invoice-settings # Invoice Settings Customise how your invoices look, what they include, and how numbering works. ## Invoice Numbering Configure the pattern for auto-generated invoice numbers: - **Format:** `INV-{YYYY}-{####}` generates numbers like INV-2024-0001, INV-2024-0002, etc. - **Variables:** - `{YYYY}` — Current year - `{####}` — Sequential number (padded to 4 digits) - **Next number:** Set the next number in the sequence (useful when migrating from another system). ## Default Payment Terms Set the default payment terms applied to new invoices: | Option | Due Date | |--------|----------| | Upon Receipt | Immediately | | Net 15 | 15 days from issue | | Net 30 | 30 days from issue | | Net 45 | 45 days from issue | | Net 60 | 60 days from issue | | Custom | Specify days | You can override the terms on individual invoices. ## Tax Configuration Set up your tax rates for automatic calculation: ### Tax 1 - **Label** — The name shown on invoices (e.g., "GST", "VAT", "Sales Tax") - **Rate** — Percentage to apply (e.g., 10%) ### Tax 2 - **Label** — Second tax name (e.g., "State Tax", "PST") - **Rate** — Percentage to apply Each line item can individually include or exclude each tax. ## Invoice Appearance Customise the look of your invoices: - **Company logo** — Uploaded image displayed on the invoice header. - **Accent colour** — The highlight colour used for headers and totals. - **Show project codes** — Include project codes alongside project names on line items. ## Email Templates Customise the default messages for invoice communications: ### Subject Template The default subject line when sending an invoice (e.g., "Invoice {number} from {company}"). ### Body Template The default email body text. Include a greeting, brief description, and payment instructions. ### Footer Text appended to all invoice emails (e.g., bank details, payment instructions, company registration). ## Item Categories Define categories for invoice line items to organise different types of charges: - Service - Product - Expense These appear as options when adding line items to an invoice. ## Payment Gateways Connect online payment processing: ### Stripe 1. Click **Connect Stripe**. 2. Authorise Keito to access your Stripe account. 3. Clients can pay by credit card or bank transfer directly from their invoice. ### PayPal 1. Enter your PayPal email or merchant ID. 2. Clients can pay via PayPal from their invoice portal. --- ## Invoicing Agent Work source: https://keito.ai/docs/invoicing/agent-work # Invoicing Agent Work Agent time and LLM expenses appear on invoices alongside human work. This page explains how to present agent work to clients clearly and professionally. ## How Agent Work Appears on Invoices When creating an invoice from tracked time and expenses: - **Agent time entries** are included like any other time entry. They show the agent's name, hours, rate, and project/task. - **LLM expenses** appear as a line item under the "LLM Usage" category with quantity, unit price, and total. ### Example Invoice Line Items | Description | Hours/Units | Rate | Amount | |---|---|---|---| | Sarah Chen — Backend development | 12.0 hrs | £150/hr | £1,800 | | review-bot-01 — Code review (Agent) | 6.0 hrs | £75/hr | £450 | | LLM Usage — claude-opus-4-6 tokens | 180k tokens | £0.003/k | £0.54 | ## Grouping Options When creating invoices, you can group line items by: - **Project** — all human and agent work under one project heading - **Team member** — agent users appear as separate line items - **Task** — human and agent contributions to the same task grouped together ## Setting Agent Rates Agent users have their own billable rates, set in [Managing Agent Users](/docs/team-management/agent-users). Common approaches: - **Discounted rate** — agents billed at a lower rate than humans (e.g., £75/hr vs £150/hr) - **Cost-plus** — LLM costs passed through plus a margin - **Blended rate** — one rate that covers both human oversight and agent execution ## Tips - Include a brief note on invoices explaining what agent work is. Many clients are seeing this for the first time. - Use the notes field on agent time entries to describe what was done in plain language. - Consider a separate line item for LLM costs so clients can see the infrastructure cost vs. the service cost. --- ## Time Reports source: https://keito.ai/docs/reports/time-reports # Time Reports Analyse how your team spends their hours across clients, projects, tasks, and team members. ## Summary Reports Summary reports aggregate time data and show totals broken down by a chosen dimension: ### By Client See total hours and billable amounts per client. Useful for understanding client revenue distribution and identifying your most active accounts. ### By Project Break down hours by project to understand where effort is being allocated. Shows billable hours, non-billable hours, and billable amounts per project. ### By Task View time distribution by task type (e.g., Design vs Development vs Meetings). Helps identify how much time goes to productive work versus overhead. ### By Team Member See who's logging what. Shows total hours, billable hours, and utilization per person. Useful for capacity planning and performance reviews. ## Detailed Time Report The detailed report shows individual time entries with full context: - Date - Team member - Client and project - Task - Notes - Hours (billable and non-billable) - Billable amount - Invoice status This is the most granular view and is useful for auditing specific entries or preparing detailed breakdowns for clients. ## Report Metrics Every time report includes these key metrics: | Metric | Description | |--------|-------------| | Total Hours | All hours tracked in the period | | Billable Hours | Hours marked as billable | | Non-Billable Hours | Hours not billable to clients | | Billable Amount | Total value of billable time (hours × rates) | | Uninvoiced Amount | Billable amount not yet included on an invoice | ## Filtering Narrow your report with any combination of filters: - **Date range** — Preset ranges (This Week, Last Month, This Quarter, etc.) or custom dates - **Client** — One or more clients - **Project** — Specific projects - **Task** — Specific task types - **Team member** — Individual people or groups - **Role** — Business roles (Designer, Developer, etc.) - **Billable status** — Billable, Non-billable, or All - **Invoice status** — Invoiced, Uninvoiced, or All - **Approval status** — Approved, Submitted, Unsubmitted, or All ## Saving Reports Save frequently-used filter combinations as named reports: 1. Configure your filters. 2. Click **Save Report**. 3. Give it a name (e.g., "Q1 Client Utilization" or "Weekly Team Hours"). 4. Access saved reports from the **Saved Reports** section. Saved reports can also be shared with other team members. ## Permissions - **Members** see only their own time in reports. - **Managers** see their own time plus their assigned teammates. - **Administrators** see all time across the workspace. --- ## Expense Reports source: https://keito.ai/docs/reports/expense-reports # Expense Reports Analyse expense spending across your organisation by client, project, category, or team member. ## Summary Reports ### By Client Total expenses per client, showing billable and non-billable breakdowns. Useful for understanding cost distribution across your client portfolio. ### By Project Expense totals per project. Helps track project-level costs and compare against budgets. ### By Category See spending by expense type (e.g., Travel, Meals, Software). Identifies the largest cost categories and spending trends. ### By Team Member Per-person expense totals. Useful for understanding who's incurring expenses and ensuring policy compliance. ## Detailed Expense Report Shows individual expense entries with: - Date - Team member - Client and project - Category - Notes - Amount - Billable status - Receipt indicator - Approval status ## Report Metrics | Metric | Description | |--------|-------------| | Total Amount | All expenses in the period | | Billable Amount | Expenses chargeable to clients | | Non-Billable Amount | Internal expenses not billed | ## Filtering Filter expense reports by: - **Date range** — Preset or custom periods - **Client** — Specific clients - **Project** — Specific projects - **Category** — Expense types - **Team member** — Individual people - **Billable status** — Billable, Non-billable, or All - **Approval status** — Approved, Submitted, Unsubmitted, or All ## Permissions - **Members** see only their own expenses. - **Managers** see expenses from their assigned teammates. - **Administrators** see all expenses in the workspace. --- ## Budget Reports source: https://keito.ai/docs/reports/budget-reports # Budget Reports Monitor budget consumption across all your projects in one view. ## Overview The Budget Report gives you a snapshot of every project with a configured budget, showing how much has been spent and how much remains. ## Report Columns For each budgeted project, you'll see: | Column | Description | |--------|-------------| | Project | Project name and client | | Budget Type | Hours, Fees, Per-Task, or Per-Person | | Total Budget | The configured budget amount | | Spent | How much has been consumed | | Remaining | Budget minus spent | | % Used | Visual progress indicator | | Monthly | Whether the budget resets monthly | | Status | On track or Over budget | ## Interpreting the Report - **Green/On Track** — Budget consumption is within acceptable limits. - **Yellow/Warning** — Approaching the notification threshold (default 80%). - **Red/Over Budget** — Spending has exceeded the budget. ## Filtering - **Date range** — For monthly budgets, filter to a specific month to see that period's consumption. - **Client** — See budgets for a specific client's projects. - **Project** — Drill into a specific project. - **Status** — Show only over-budget or on-track projects. ## Using the Budget Report Common use cases: - **Weekly check-in** — Review all active budgets to catch potential overruns early. - **Client meetings** — Show clients how their budget is being consumed. - **Resource planning** — Identify projects nearing capacity that may need additional budget or scope reduction. - **Month-end review** — For monthly budgets, review consumption before the reset. ## Related - [Setting up project budgets](/docs/projects/budgets) - [Over-budget notifications](/docs/projects/budgets#over-budget-notifications) --- ## Exporting Data source: https://keito.ai/docs/reports/exporting-data # Exporting Data Export your Keito data for use in spreadsheets, accounting software, or custom analysis tools. ## Export Formats Keito supports two export formats: ### CSV A universal plain-text format compatible with any spreadsheet application. Best for simple data analysis or importing into other systems. ### Excel (.xlsx) A formatted spreadsheet with proper column types, headers, and styling. Best for sharing polished reports or doing advanced analysis in Excel/Google Sheets. ## What You Can Export ### Time Entries Export tracked time with the following columns: - Date, Client, Project, Task, Team Member - Notes, Hours, Billable (Yes/No) - Billed (Yes/No), Hourly Rate, Billable Amount **Filters:** Date range, project, client, team member, billable status, approval status. ### Expenses Export expense records including: - Date, Client, Project, Category, Team Member - Notes, Amount, Billable (Yes/No) - Billed (Yes/No), Receipt (Yes/No) **Filters:** Date range, project, client, category, approval status. ### Projects Export your project list with: - Name, Client, Project Type, Budget - Spent, Remaining, Status (Active/Archived) **Filters:** Active, Archived, Budgeted. ### Invoices Export invoice data including: - Invoice Number, Client, Total Amount - State (Draft/Open/Paid/Closed), Due Date, Balance **Filters:** State, date range, client. ### Clients Export your client directory: - Name, Address, Currency, Status (Active/Archived) **Filters:** Active, Archived. ### Team Export team member information: - Name, Email, Role, Weekly Capacity - Hourly Rate, Contractor/Employee **Filters:** Active, Deactivated. ## How to Export 1. Navigate to the data section you want to export (e.g., Reports, Projects, Invoices). 2. Apply any filters to narrow the data set. 3. Click the **Export** button. 4. Choose your format (CSV or Excel). 5. The file downloads to your device. ## Access Control - **Members** can export their own data only. - **Administrators** can export all company data. Exported files contain only the data you have permission to view. ## Large Exports For workspaces with large amounts of data, exports are processed with streaming to handle the volume efficiently. There's no hard limit on export size. ## Tips - Apply filters before exporting to get exactly the data you need. - Use CSV for importing into other tools (accounting software, project management systems). - Use Excel for polished reports to share with stakeholders. - Regular exports can serve as data backups for your records. --- ## Agent Activity Reports source: https://keito.ai/docs/reports/agent-activity # Agent Activity Reports Keito's reporting tools let you analyse agent productivity, costs, and ROI alongside human work. ## Filtering Reports by Source All report types (Time, Expense, Budget) support source filtering: - Select **Source: Agent** to see only agent-generated data. - Select **Source: All** and group by source to compare human vs. agent side by side. ## Key Metrics for Agent Work ### Time Analysis - **Agent hours by project** — which projects use agents most? - **Agent vs. human hours** — what's the ratio? - **Agent hours by model** — filter metadata to see which LLM models are used. ### Cost Analysis - **LLM spend by project** — where are tokens being consumed? - **LLM spend by model** — which models are cost-effective? - **Agent billable revenue vs. LLM cost** — what's the margin? ### ROI Analysis - **Agent billable rate × hours** vs. **LLM cost** = agent margin - Compare agent cost-per-task vs. human cost-per-task for similar work. ## Exporting Agent Data Use **Reports → Exporting Data** to export agent-filtered data as CSV for further analysis. The export includes source and metadata fields. --- ## Roles & Permissions source: https://keito.ai/docs/team-management/roles-permissions # Roles & Permissions Keito uses a role-based permission system to control what team members can see and do within your workspace. ## Permission Levels ### Member The default role for team members. Members can: - Track their own time and expenses - View their own reports - Edit their own profile - See projects they're assigned to - Submit timesheets for approval ### Manager An elevated role for team leads and project managers. In addition to Member permissions, Managers can: - View time and expenses from their assigned teammates - Approve or reject submitted timesheets - See reports for their assigned team - Manage projects they're assigned to as project manager ### Administrator Full access to all workspace features. Administrators can: - View and edit all time entries and expenses across the workspace - Approve any timesheet - Create, edit, and archive projects, clients, and tasks - Manage team members (invite, edit, archive) - Configure workspace settings - Manage integrations - Access all reports with full data - Manage billing and subscription ## Granular Manager Permissions Administrators can customise exactly what managers can do by toggling specific permissions: | Permission | Description | |-----------|-------------| | Create projects | Create new projects for their clients | | View/edit billable rates | See and modify hourly rates | | Draft invoices | Create invoice drafts for managed projects | | Send/edit invoices | Send and modify invoices for managed projects | | Create/edit clients and tasks | Manage the client and task directory | | Edit team time and expenses | Modify entries belonging to assigned teammates | | Withdraw approvals | Undo a previous timesheet approval | These permissions are configured per-manager from the Team management page. ## Teammate Assignments Managers can be assigned specific teammates, limiting their visibility and approval authority: - A manager only sees time/expenses from their assigned teammates. - They can only approve timesheets from their assigned teammates. - They only see their assigned teammates in reports. If no teammates are assigned, the manager has no team visibility (beyond their own data). ## Business Roles Separate from permission levels, business roles are organizational labels used for reporting and filtering: - Examples: Designer, Developer, QA Engineer, Project Manager, Consultant - Users can have multiple business roles. - Roles don't affect permissions — they're purely for categorization. - Use roles in report filters to analyse time by discipline. Manage business roles from **Manage > Roles**. ## Role Assignment Roles are assigned when inviting a team member or can be changed later by an administrator: 1. Go to **Team**. 2. Click on a team member. 3. Change their permission level (Member, Manager, Administrator). 4. For managers, configure granular permissions and teammate assignments. --- ## Managing Members source: https://keito.ai/docs/team-management/managing-members # Managing Members Manage your team's profiles, rates, and lifecycle within Keito. ## User Properties Each team member has the following profile information: ### Personal Details - **First name** and **Last name** - **Email address** — Used for login and notifications - **Phone number** — Optional contact number - **Timezone** — Their local timezone for scheduling and reminders - **Employee ID** — Optional internal identifier ### Employment Details - **Engagement type** — Employee or Contractor - **Weekly capacity** — Expected work hours per week (used for utilization calculations) - **Permission level** — Member, Manager, or Administrator - **Business roles** — Organizational labels (Designer, Developer, etc.) ### Rate Information - **Default hourly rate** — The billing rate used unless overridden at the project level - **Cost rate** — Internal cost rate for profitability calculations (visible to admins only) ## User Lifecycle ### Pending When someone is invited but hasn't yet accepted: - They appear in the team list as "Pending." - They cannot log in or track time. - You can resend their invitation or cancel it. ### Active Once they accept the invitation: - They can log in and use Keito. - They appear in project assignment dropdowns. - They count toward your subscription seat count. ### Archived When someone leaves the team: 1. Go to **Team** and select the member. 2. Click **Archive**. 3. The member can no longer log in. 4. Their historical data (time entries, expenses, invoices) is preserved. 5. They no longer count toward your seat count. Archived members can be reactivated at any time. ## Editing Member Profiles Administrators can edit any team member's profile: 1. Go to **Team**. 2. Click on the member's name. 3. Update their details. 4. Save changes. Members can edit their own profile (name, timezone, notification preferences) but cannot change their role or rates. ## Contractor vs Employee The contractor/employee distinction affects: - **Reporting** — Filter the Contractor Report to see hours and costs for contractors specifically. - **Capacity** — Contractors may have different weekly capacity expectations. - **Cost tracking** — Different cost rate structures for profitability analysis. This is a classification label and doesn't affect permissions or functionality. ## Bulk Management For large teams: - Use bulk invite to onboard multiple people at once. - Filter the team list by role, status, or business role. - Export team data for external analysis. --- ## Capacity Planning source: https://keito.ai/docs/team-management/capacity-planning # Capacity Planning Track team utilization and ensure workloads are distributed effectively across your team. ## Weekly Capacity Each team member has a configured weekly capacity — the number of hours they're expected to work per week. This serves as the baseline for utilization calculations. ### Setting Capacity - **Workspace default** — Set a default weekly capacity in Settings (e.g., 40 hours). - **Per-person override** — Customise capacity for individuals who work different schedules (e.g., part-time at 20 hours, overtime at 45 hours). ### Where Capacity Is Used - **Time page** — Weekly totals show progress toward capacity. - **Reports** — Utilization percentages compare tracked hours against capacity. - **Team overview** — See who's over or under their expected hours. ## Utilization Utilization measures how much of a person's capacity is being used: ``` Utilization = Tracked Hours / Weekly Capacity × 100% ``` ### Billable Utilization A more focused metric that looks at only billable hours: ``` Billable Utilization = Billable Hours / Weekly Capacity × 100% ``` This tells you how much of someone's time is generating revenue. ## Understanding the Numbers | Utilization | Interpretation | |-------------|---------------| | Below 70% | Potentially underutilized — may have capacity for more work | | 70-90% | Healthy range — productive with room for non-project work | | 90-100% | Fully loaded — little room for unexpected work | | Above 100% | Overloaded — working beyond expected capacity | These ranges vary by industry and role. A developer might target 75% billable utilization to allow time for learning and internal work, while a consultant might target 85%. ## Using Reports for Capacity Planning ### Team Utilization Report Run a Time report grouped by Team Member to see: - Total hours per person - Billable vs non-billable breakdown - Compare against capacity targets ### Identifying Issues - **Consistently under capacity** — May need more project assignments or the capacity setting is too high. - **Consistently over capacity** — Risk of burnout. Consider redistributing work or adjusting the capacity expectation. - **High total hours, low billable** — Too much time on internal/non-billable work. ## Tips - Review capacity weekly during team standups. - Adjust capacity settings when team members change their working patterns (e.g., going part-time). - Use the Week View to spot days with unusually low or high tracking. - Factor in holidays and PTO when evaluating monthly utilization. --- ## Managing Agent Users source: https://keito.ai/docs/team-management/agent-users # Managing Agent Users Agent users are profiles that represent AI agents in your workspace. They have names, rates, and project assignments — just like human team members. ## Creating an Agent User 1. Go to **Team** → **Add Member**. 2. Set the user type to **Agent**. 3. Enter a name for the agent (e.g., "review-bot-01", "code-agent"). 4. Set the billable rate and cost rate. 5. Assign the agent to projects. ## Agent vs. Human Users | Feature | Human User | Agent User | |---|---|---| | Can log in to UI | Yes | No | | Has API key | Optional | Yes | | Billable rate | Yes | Yes | | Project assignment | Yes | Yes | | Approval workflow | Yes | Yes | | Shows on invoices | Yes | Yes (with Agent badge) | | Capacity planning | Yes | Optional | ## Best Practices - **One agent user per logical agent.** If you have a code review bot and a documentation bot, create separate agent users for each. - **Set appropriate rates.** Agent rates are typically lower than human rates. Consider what the client will accept. - **Assign to specific projects.** Agents should only track time against projects they're assigned to, just like humans. - **Review agent entries regularly.** Include agent timesheets in your weekly approval cycle. --- ## Managing Clients source: https://keito.ai/docs/clients/managing-clients # Managing Clients Clients represent the organizations you do work for. Every project belongs to a client, connecting your tracked time and expenses to the right billing entity. ## Creating a Client 1. Go to **Manage > Clients**. 2. Click **New Client**. 3. Enter the client details: - **Name** — The client's company or organisation name (required) - **Address** — Street address, city, state/province, postal code, country - **Phone** — Contact phone number - **Currency** — The currency used for billing this client 4. Click **Save**. ## Client Contacts Add one or more contacts for each client: - **First name** and **Last name** - **Title** — Job title or role - **Email** — Used for sending invoices - **Phone numbers** — Office, mobile, and fax Contacts receive invoice emails and are the primary point of communication for billing. ### Adding a Contact 1. Open the client. 2. Go to the **Contacts** section. 3. Click **Add Contact**. 4. Fill in their details and save. ## Client Currency Each client has an associated currency that determines: - How amounts are displayed on their invoices - The currency symbol shown in reports filtered by that client - Financial calculations for that client's projects Set this when creating the client and it applies to all their projects and invoices. ## Editing Clients 1. Go to **Manage > Clients**. 2. Click on the client name. 3. Update any fields. 4. Save changes. ## Archiving Clients When you're no longer actively working with a client: 1. Open the client. 2. Click **Archive**. Archived clients: - Don't appear in project creation dropdowns. - Don't appear in active client lists. - Retain all historical data (projects, time, invoices). - Can be restored at any time. ## Restoring Clients To bring back an archived client: 1. Go to **Manage > Clients**. 2. Switch to the **Archived** view. 3. Find the client and click **Restore**. The client returns to active status and is available for new projects. ## Deleting Clients Clients can only be deleted if they have no associated data (projects, invoices, time entries). In practice, archiving is preferred over deletion since it preserves history. ## Clients and Projects Every project requires a client association. When you create a new project, you'll select from your active client list. This relationship: - Groups projects by client in reports and filters. - Determines which client is billed on invoices. - Applies the client's currency to financial calculations. ## Tips - Set up your client list before creating projects. - Use the address field — it appears on invoices. - Add contacts with email addresses so you can send invoices directly from Keito. - Archive rather than delete when a client engagement ends. --- ## Xero Accounting Integration source: https://keito.ai/docs/integrations/xero # Xero Accounting Integration Connect Keito to Xero to sync invoices and keep your accounting records in sync without manual data entry. ## Overview > **Note:** Only one accounting integration can be active at a time. If you're currently connected to QuickBooks, you'll need to disconnect before connecting Xero. The Xero integration allows you to: - Push invoices from Keito directly into Xero - Automatically create Xero contacts from your Keito clients - Map tax rates between the two systems - Record payments in Xero when they're logged in Keito - Track sync status for every invoice ## Requirements - A Keito Business plan subscription - A Xero account with appropriate permissions - Administrator access in Keito ## Connecting Xero 1. Go to **Settings > Integrations > Xero**. 2. Click **Connect to Xero**. 3. You'll be redirected to Xero to authorise the connection. 4. Grant Keito access to your Xero organisation. 5. You'll be returned to Keito with the connection active. The connection uses OAuth 2.0 with automatic token refresh, so you won't need to reconnect regularly. ## Configuration After connecting, configure your sync preferences: ### Revenue Account Select the default Xero account where invoice revenue should be recorded (e.g., "Sales" or "Professional Services Revenue"). ### Payment Account Optionally select the bank account where payments are recorded when you mark invoices as paid. ### Tax Rates Keito fetches your Xero tax rates so you can map them to your Keito tax settings. This ensures tax calculations match between systems. ### Tracking Categories If you use Xero tracking categories for departmental or regional reporting, you can configure how Keito data maps to them. ## Syncing Invoices ### Sending an Invoice to Xero 1. Open a finalized invoice in Keito. 2. Click **Copy to Xero**. 3. Keito creates the invoice in Xero with matching: - Invoice number - Client (as a Xero contact) - Line items with descriptions, quantities, and amounts - Tax rates - Due date and payment terms ### Contact Sync When you sync an invoice for a client that doesn't exist in Xero: - Keito automatically creates a new Xero contact using your client's name and details. - The Xero contact ID is stored so future invoices for that client are linked correctly. ### Sync Status Each invoice shows its Xero sync status: | Status | Meaning | |--------|---------| | Not synced | Invoice hasn't been sent to Xero | | Pending | Sync is in progress | | Success | Successfully created in Xero | | Failed | Sync failed — check the error details | If a sync fails, you can view the error message and retry. ## Payment Sync When you record a payment in Keito, it can be reflected in Xero: - The Xero invoice is updated to show the payment. - Transaction IDs are stored for reconciliation. - Partial payments are supported. ## Disconnecting To remove the Xero integration: 1. Go to **Settings > Integrations > Xero**. 2. Click **Disconnect**. 3. The OAuth tokens are revoked. 4. Previously synced data remains in both systems but no further syncing occurs. ## Troubleshooting ### Token Expired If the connection shows as expired, click **Reconnect** to re-authorise. This can happen if the refresh token (valid for 60 days) expires due to extended inactivity. ### Sync Failures Common causes of sync failures: - **Missing Xero account** — Ensure your revenue account is configured. - **Tax rate mismatch** — Verify tax rate mappings are correct. - **Xero permissions** — Ensure your Xero user has invoice creation permissions. - **Rate limiting** — Xero has API rate limits. Retry after a brief wait. ### Duplicate Invoices Keito tracks which invoices have been synced and stores the Xero invoice ID. If you see duplicates, check the sync status before re-syncing. --- ## QuickBooks Online Integration source: https://keito.ai/docs/integrations/quickbooks # QuickBooks Online Integration Connect Keito to QuickBooks Online (QBO) to sync invoices, reconcile payments, map employees, and push time entries — without manual data entry. ## Overview > **Note:** Only one accounting integration can be active at a time. If you're currently connected to Xero, you'll need to disconnect before connecting QuickBooks. The QuickBooks integration allows you to: - Push invoices from Keito directly into QuickBooks Online - Automatically create QBO customers from your Keito clients - Reconcile payments from QuickBooks back into Keito - Sync approved time entries as QBO TimeActivity records - Map Keito tasks to QBO Products/Services (items) - Map Keito team members to QBO employees - Optionally create sub-customers for project-level tracking ## Requirements - A Keito Business plan subscription - A QuickBooks Online account with admin permissions - Administrator access in Keito ## Connecting QuickBooks 1. Go to **Settings > Integrations > QuickBooks**. 2. Click **Connect to QuickBooks**. 3. You'll be redirected to Intuit to authorise the connection. 4. Grant Keito access to your QuickBooks company. 5. You'll be returned to Keito with the connection active. The connection uses OAuth 2.0 with automatic token refresh, so you won't need to reconnect regularly. ## Configuration After connecting, configure your sync preferences: ### Default Income Account Select the QBO income account where invoice revenue should be recorded (e.g., "Sales" or "Services Revenue"). This account is used for invoice line items when syncing to QuickBooks. ### Deposit Account Select the bank account where payments are deposited. When payments are synced from QuickBooks, they'll be routed to this account. ### Task-to-Item Mapping Map your Keito tasks to QuickBooks Products/Services (items). This controls how invoice line items appear in QBO: - Navigate to **Settings > Integrations > QuickBooks > Item Mapping**. - Each Keito task can be mapped to a specific QBO service or product item. - Unmapped tasks will use the default income account. ### Employee Mapping Map Keito team members to QuickBooks employees. This is required for time entry sync: - Navigate to **Settings > Integrations > QuickBooks > Employee Mapping**. - Use **Auto-Match** to automatically pair members with QBO employees by name. - Manually adjust any mappings that weren't matched automatically. ### Sub-customer Support Optionally map Keito projects as QBO sub-customers under the parent client customer. This is useful for tracking revenue at the project level in QuickBooks. ## Syncing Invoices ### Sending an Invoice to QuickBooks 1. Open a finalised invoice in Keito. 2. Click **Copy to QuickBooks**. 3. Keito creates the invoice in QuickBooks with matching: - Invoice number - Client (as a QBO customer) - Line items with descriptions, quantities, and amounts - Task-to-item mappings - Due date and payment terms ### Customer Sync When you sync an invoice for a client that doesn't exist in QuickBooks: - Keito automatically creates a new QBO customer using your client's name and details. - The QBO customer ID is stored so future invoices for that client are linked correctly. ### Sync Status Each invoice shows its QuickBooks sync status: | Status | Meaning | |--------|---------| | Not synced | Invoice hasn't been sent to QuickBooks | | Pending | Sync is in progress | | Success | Successfully created in QuickBooks | | Failed | Sync failed — check the error details | If a sync fails, you can view the error message and retry. ## Payment Reconciliation Payments recorded in QuickBooks can be synced back to Keito: - **Single sync** — Sync an individual payment from a specific invoice. - **Bulk sync** — Sync all outstanding payments in one action. - Payments are routed to the configured deposit account. - Transaction IDs are stored for reconciliation. - Partial payments are supported. ## Time Entry Sync Push approved time entries from Keito into QuickBooks as TimeActivity records: - Time entries are synced with the associated employee, customer, and billable status. - Hourly rates from Keito are included on synced entries. - **Employee mapping must be configured** before time entries can be synced. - Only approved time entries are eligible for sync. ## Disconnecting To remove the QuickBooks integration: 1. Go to **Settings > Integrations > QuickBooks**. 2. Click **Disconnect**. 3. The OAuth tokens are revoked. 4. Previously synced data remains in both systems but no further syncing occurs. ## Troubleshooting ### Token Expired If the connection shows as expired, click **Reconnect** to re-authorise. This can happen if the refresh token expires due to extended inactivity. ### Sync Failures Common causes of sync failures: - **Missing income account** — Ensure your default income account is configured. - **Item mapping incomplete** — Verify task-to-item mappings are set for all tasks used on the invoice. - **QuickBooks permissions** — Ensure your QBO user has invoice and payment permissions. - **Rate limiting** — QuickBooks has API rate limits. Retry after a brief wait. ### Duplicate Invoices Keito tracks which invoices have been synced and stores the QuickBooks invoice ID. If you see duplicates, check the sync status before re-syncing. ### Time Entry Errors - Ensure the team member is mapped to a QBO employee before syncing time entries. - Verify the associated customer exists in QuickBooks (sync an invoice first if needed). --- ## Introduction source: https://keito.ai/docs/developer/introduction # Introduction Keito's API lets you track time, log expenses, and manage projects programmatically. The most common use case: connecting AI agents so they can log their own work — just like a human team member. ## Why Integrate? - **Agent accountability** — every hour of agent work is tracked, approved, and invoiced. - **LLM cost allocation** — token costs are assigned to projects and passed through to clients. - **Unified reporting** — one dashboard for human and agent productivity. ## Integration Options | Method | Best for | Language | |---|---|---| | **REST API** | Any language, direct HTTP calls | Any | | **Node SDK** | TypeScript/JavaScript apps, agent orchestration | TypeScript/JS | | **Python SDK** | Python agents, LangChain, CrewAI, notebooks | Python | | **CLI** | Terminal workflows, CI/CD pipelines | Shell | ## Core Concepts ### Sources Every time entry and expense has a `source` field: - `web` — created in the Keito web app - `cli` — created via the Keito CLI - `api` — created via a direct REST API call - `agent` — created by an AI agent ### Metadata Agent entries include a `metadata` JSON object with structured context: ```json { "agent_id": "review-bot-01", "agent_type": "claude-code", "session_id": "550e8400-e29b-41d4-a716-446655440000", "model": "claude-opus-4-6" } ``` Metadata is indexed, filterable, and visible in reports. Maximum size: 4KB. ### User Types Users are either `human` or `agent`. Agent users have API keys, rates, and project assignments — but they don't log into the web UI. ## Next Steps - [Quickstart](/docs/developer/quickstart) — create your first agent time entry in under 5 minutes. - [Authentication](/docs/auth/api-keys) — get your API key. - [REST API Reference](/docs/api-reference/overview) — full endpoint documentation. --- ## Quickstart source: https://keito.ai/docs/developer/quickstart # Quickstart Create your first agent time entry in under 5 minutes. ## Prerequisites - A Keito workspace (sign up at keito.ai if you don't have one) - An API key (see [Authentication](/docs/auth/api-keys)) - An agent user created in your workspace ## Step 1: Get Your API Key Navigate to **Settings → API** in the Keito web app and create a new API key. Copy it — you'll need it for every request. ## Step 2: Create a Time Entry ### cURL ```bash curl -X POST https://api.keito.ai/v1/time-entries \ -H "Authorization: Bearer YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "project_id": "prj_abc", "task_id": "tsk_001", "spent_date": "2026-03-06", "hours": 1.5, "notes": "Implemented OAuth flow", "source": "agent", "metadata": { "agent_id": "my-bot-01", "agent_type": "claude-code", "session_id": "550e8400-e29b-41d4-a716-446655440000", "model": "claude-opus-4-6" } }' ``` ### Node SDK ```typescript import { Keito } from '@keito/sdk'; const keito = new Keito({ apiKey: process.env.KEITO_API_KEY, accountId: process.env.KEITO_ACCOUNT_ID, }); const entry = await keito.timeEntries.create({ project_id: 'prj_abc', task_id: 'tsk_001', spent_date: '2026-03-06', hours: 1.5, notes: 'Implemented OAuth flow', source: 'agent', metadata: { agent_id: 'my-bot-01', agent_type: 'claude-code', session_id: crypto.randomUUID(), model: 'claude-opus-4-6', }, }); console.log(`Created entry: ${entry.id}`); ``` ### Python SDK ```python import os from keito import Keito client = Keito( api_key=os.environ["KEITO_API_KEY"], account_id=os.environ["KEITO_ACCOUNT_ID"], ) entry = client.time_entries.create( project_id="prj_abc", task_id="tsk_001", spent_date="2026-03-06", hours=1.5, notes="Implemented OAuth flow", source="agent", metadata={ "agent_id": "my-bot-01", "agent_type": "claude-code", "session_id": "550e8400-e29b-41d4-a716-446655440000", "model": "claude-opus-4-6", }, ) print(f"Created entry: {entry.id}") ``` ### CLI ```bash keito time log --project acme --task development \ --hours 1.5 \ --notes "Implemented OAuth flow" \ --source agent \ --agent-id my-bot-01 ``` ## Step 3: Log an LLM Expense After the agent finishes work, log the token cost: ### cURL ```bash curl -X POST https://api.keito.ai/v1/expenses \ -H "Authorization: Bearer YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "project_id": "prj_abc", "expense_category_id": "cat_llm_usage", "spent_date": "2026-03-06", "units": 45, "notes": "claude-opus-4-6: 30k input + 15k output tokens", "source": "agent", "metadata": { "agent_id": "my-bot-01", "session_id": "550e8400-e29b-41d4-a716-446655440000", "model": "claude-opus-4-6", "input_tokens": 30000, "output_tokens": 15000 } }' ``` ### Node SDK ```typescript await keito.expenses.create({ project_id: 'prj_abc', expense_category_id: 'cat_llm_usage', spent_date: '2026-03-06', units: 45, notes: 'claude-opus-4-6: 30k input + 15k output tokens', source: 'agent', metadata: { agent_id: 'my-bot-01', session_id: entry.metadata.session_id, model: 'claude-opus-4-6', input_tokens: 30000, output_tokens: 15000, }, }); ``` ### Python SDK ```python client.expenses.create( project_id="prj_abc", expense_category_id="cat_llm_usage", spent_date="2026-03-06", units=45, notes="claude-opus-4-6: 30k input + 15k output tokens", source="agent", metadata={ "agent_id": "my-bot-01", "session_id": entry.metadata["session_id"], "model": "claude-opus-4-6", "input_tokens": 30000, "output_tokens": 15000, }, ) ``` ### CLI ```bash keito expense log --project acme --quantity 45 \ --notes "claude-opus-4-6: 30k input + 15k output tokens" ``` ## Step 4: Verify in the UI Open the Keito web app. Navigate to your project's time entries. You should see your new entry with a violet **Agent** badge. ## What's Next? - [Agent Integration Patterns](/docs/agent-integration/overview) — learn the start/stop timer pattern for real-time tracking. - [Node SDK Guide](/docs/node-sdk/getting-started) — full SDK setup. - [Python SDK Guide](/docs/python-sdk/getting-started) — full SDK setup. - [CLI Guide](/docs/cli/getting-started) — install and configure the CLI. --- ## Concepts source: https://keito.ai/docs/developer/concepts # Concepts Key concepts for working with the Keito API. ## Time Entries A time entry records work done by a human or agent. Key fields: | Field | Type | Description | |---|---|---| | `id` | string | Unique identifier | | `project_id` | string | The project this work is for | | `task_id` | string | The task within the project (optional) | | `user_id` | string | The human or agent user | | `spent_date` | string | Date the work was performed (YYYY-MM-DD) | | `hours` | number | Duration in decimal hours | | `notes` | string | Description of work done | | `is_running` | boolean | Whether a timer is currently active | | `is_billable` | boolean | Whether this entry is billable | | `source` | string | Origin: `web`, `cli`, `api`, `agent` | | `metadata` | object | Agent context (JSON, max 4KB) | ## Expenses An expense records a cost against a project. For agent work, this is typically LLM token usage. | Field | Type | Description | |---|---|---| | `id` | string | Unique identifier | | `project_id` | string | The project this cost is for | | `expense_category_id` | string | Category (e.g., "LLM Usage") | | `spent_date` | string | Date of the expense (YYYY-MM-DD) | | `units` | number | Quantity (e.g., tokens in thousands) | | `unit_price` | number | Price per unit | | `total_cost` | number | Calculated total | | `notes` | string | Description | | `source` | string | Origin: `web`, `cli`, `api`, `agent` | | `metadata` | object | Agent context (JSON, max 4KB) | ## Users | Field | Type | Description | |---|---|---| | `id` | string | Unique identifier | | `name` | string | Display name | | `email` | string | Email (agents may use placeholder) | | `user_type` | string | `human` or `agent` | | `is_active` | boolean | Whether the user is active | ## Source Values | Source | Created By | Badge Colour | |---|---|---| | `web` | Keito web app | None | | `cli` | Keito CLI | Amber | | `api` | Direct REST API call | None | | `agent` | AI agent via API or SDK | Violet | ## Metadata Schema The `metadata` field is a freeform JSON object (max 4KB). Recommended fields for agent entries: ```json { "agent_id": "string — unique identifier for this agent", "agent_type": "string — platform: claude-code, codex, cursor, etc.", "session_id": "string — UUID grouping related entries", "model": "string — LLM model used", "input_tokens": "number — input tokens consumed (expenses only)", "output_tokens": "number — output tokens consumed (expenses only)" } ``` Use `session_id` to correlate a time entry with its corresponding LLM expense. --- ## Authentication source: https://keito.ai/docs/auth/api-keys # Authentication All API requests require authentication via an API key passed in the `Authorization` header. ## Creating an API Key 1. Log in to the Keito web app. 2. Navigate to **Settings → API**. 3. Click **Create API Key**. 4. Give the key a descriptive name (e.g., "Claude Code Agent", "CI Pipeline"). 5. Copy the key immediately — it won't be shown again. ## Using Your API Key Include the key in the `Authorization` header of every request: ```bash curl -X GET https://api.keito.ai/v1/time-entries \ -H "Authorization: Bearer keito_sk_abc123..." ``` In the SDKs: ```typescript // Node SDK const keito = new Keito({ apiKey: process.env.KEITO_API_KEY, accountId: process.env.KEITO_ACCOUNT_ID, }); ``` ```python # Python SDK client = Keito( api_key=os.environ["KEITO_API_KEY"], account_id=os.environ["KEITO_ACCOUNT_ID"], ) ``` ## Environment Variables Both SDKs read these environment variables automatically: | Variable | Description | |---|---| | `KEITO_API_KEY` | Your API key | | `KEITO_ACCOUNT_ID` | Your workspace/account ID | If set, you can initialise the client with no arguments: ```typescript const keito = new Keito(); // reads from env ``` ## Security Best Practices - Never commit API keys to source control. - Use environment variables or a secrets manager. - Create separate keys for each agent or integration. - Rotate keys periodically. - Revoke keys immediately if compromised. ## Rate Limits | Plan | Requests/minute | Requests/day | |---|---|---| | Free | 60 | 1,000 | | Pro | 300 | 50,000 | | Enterprise | Custom | Custom | Rate-limited responses return HTTP `429` with a `Retry-After` header. --- ## Scopes & Permissions source: https://keito.ai/docs/auth/scopes # Scopes & Permissions API keys inherit the permissions of the user they belong to. An agent user's API key can only access projects the agent is assigned to. ## Permission Model | Role | Can create entries | Can approve | Can manage users | Can invoice | |---|---|---|---|---| | Member | Own entries only | No | No | No | | Manager | Own + team | Yes | No | Yes | | Administrator | All | Yes | Yes | Yes | Agent users are typically created as **Members** — they can create time entries and expenses for their assigned projects, but they cannot approve timesheets or manage other users. ## Project Scoping An API key can only interact with projects the associated user is assigned to. Attempting to create a time entry for an unassigned project returns HTTP `403`. --- ## REST API Reference source: https://keito.ai/docs/api-reference/overview # REST API Reference Base URL: `https://api.keito.ai/v1` All requests and responses use JSON. Authenticate with a Bearer token in the `Authorization` header. ## Conventions - **Dates** — ISO 8601 format: `YYYY-MM-DD` for dates, full ISO for timestamps. - **IDs** — prefixed strings: `prj_`, `tsk_`, `usr_`, `te_`, `exp_`. - **Pagination** — cursor-based. Responses include `next_cursor` when more results exist. - **Errors** — consistent error object (see [Error Handling](/docs/api-reference/error-handling)). ## Endpoints | Method | Endpoint | Description | |---|---|---| | `POST` | `/v1/time-entries` | Create a time entry | | `GET` | `/v1/time-entries` | List time entries | | `GET` | `/v1/time-entries/:id` | Get a single time entry | | `PATCH` | `/v1/time-entries/:id` | Update a time entry | | `DELETE` | `/v1/time-entries/:id` | Delete a time entry | | `POST` | `/v1/expenses` | Create an expense | | `GET` | `/v1/expenses` | List expenses | | `GET` | `/v1/expenses/:id` | Get a single expense | | `GET` | `/v1/projects` | List projects | | `GET` | `/v1/projects/:id` | Get a single project | | `GET` | `/v1/users` | List users | | `GET` | `/v1/users/:id` | Get a single user | ## Common Query Parameters | Parameter | Type | Description | |---|---|---| | `source` | string | Filter by source: `web`, `cli`, `api`, `agent` | | `project_id` | string | Filter by project | | `user_id` | string | Filter by user | | `from` | string | Start date (YYYY-MM-DD) | | `to` | string | End date (YYYY-MM-DD) | | `cursor` | string | Pagination cursor | | `limit` | number | Results per page (default 50, max 200) | --- ## Time Entries API source: https://keito.ai/docs/api-reference/time-entries # Time Entries API ## Create a Time Entry `POST /v1/time-entries` Creates a new time entry. For agent integrations, set `source` to `"agent"` and include `metadata` with agent context. ### Request Body | Field | Type | Required | Description | |---|---|---|---| | `project_id` | string | Yes | Project ID | | `task_id` | string | No | Task ID within the project | | `spent_date` | string | Yes | Date of work (YYYY-MM-DD) | | `hours` | number | Yes* | Duration in decimal hours | | `is_running` | boolean | No | Start a running timer (default false) | | `notes` | string | No | Description of work | | `is_billable` | boolean | No | Billable status (default from project) | | `source` | string | No | `web`, `cli`, `api`, `agent` (default `api`) | | `metadata` | object | No | Agent context JSON (max 4KB) | *If `is_running` is `true`, `hours` defaults to `0` and increments automatically. ### Example Request ```bash curl -X POST https://api.keito.ai/v1/time-entries \ -H "Authorization: Bearer keito_sk_abc123" \ -H "Content-Type: application/json" \ -d '{ "project_id": "prj_abc", "task_id": "tsk_001", "spent_date": "2026-03-06", "hours": 1.5, "notes": "Refactored authentication module", "source": "agent", "metadata": { "agent_id": "review-bot-01", "agent_type": "claude-code", "session_id": "550e8400-e29b-41d4-a716-446655440000", "model": "claude-opus-4-6" } }' ``` ### Example Response ```json { "id": "te_abc123", "project_id": "prj_abc", "task_id": "tsk_001", "user_id": "usr_agent_01", "spent_date": "2026-03-06", "hours": 1.5, "notes": "Refactored authentication module", "is_running": false, "is_billable": true, "source": "agent", "metadata": { "agent_id": "review-bot-01", "agent_type": "claude-code", "session_id": "550e8400-e29b-41d4-a716-446655440000", "model": "claude-opus-4-6" }, "created_at": "2026-03-06T14:30:00Z", "updated_at": "2026-03-06T14:30:00Z" } ``` ## List Time Entries `GET /v1/time-entries` Returns a paginated list of time entries. Supports filtering by source, project, user, and date range. ### Query Parameters | Parameter | Type | Description | |---|---|---| | `source` | string | Filter by source | | `project_id` | string | Filter by project | | `user_id` | string | Filter by user | | `from` | string | Start date (inclusive) | | `to` | string | End date (inclusive) | | `is_running` | boolean | Filter running timers | | `cursor` | string | Pagination cursor | | `limit` | number | Results per page (default 50, max 200) | ### Example: List Agent Entries for a Project ```bash curl "https://api.keito.ai/v1/time-entries?source=agent&project_id=prj_abc&from=2026-03-01&to=2026-03-06" \ -H "Authorization: Bearer keito_sk_abc123" ``` ### Example Response ```json { "data": [ { "id": "te_abc123", "project_id": "prj_abc", "hours": 1.5, "source": "agent", "metadata": { "agent_id": "review-bot-01" }, "spent_date": "2026-03-06" } ], "next_cursor": null, "has_more": false } ``` ## Get a Time Entry `GET /v1/time-entries/:id` Returns a single time entry by ID. ## Update a Time Entry `PATCH /v1/time-entries/:id` Updates a time entry. Commonly used to stop a running timer: ```bash curl -X PATCH https://api.keito.ai/v1/time-entries/te_abc123 \ -H "Authorization: Bearer keito_sk_abc123" \ -H "Content-Type: application/json" \ -d '{ "hours": 1.5, "is_running": false, "notes": "Completed OAuth implementation" }' ``` ### Updatable Fields | Field | Type | Description | |---|---|---| | `hours` | number | Updated duration | | `notes` | string | Updated description | | `is_running` | boolean | Stop a running timer | | `is_billable` | boolean | Change billable status | | `task_id` | string | Move to a different task | | `metadata` | object | Update agent context | Note: `source` and `project_id` cannot be changed after creation. ## Delete a Time Entry `DELETE /v1/time-entries/:id` Permanently deletes a time entry. Use for discarding failed agent sessions. ```bash curl -X DELETE https://api.keito.ai/v1/time-entries/te_abc123 \ -H "Authorization: Bearer keito_sk_abc123" ``` Returns HTTP `204 No Content` on success. --- ## Expenses API source: https://keito.ai/docs/api-reference/expenses # Expenses API ## Create an Expense `POST /v1/expenses` Creates a new expense. For LLM cost tracking, use the "LLM Usage" expense category. ### Request Body | Field | Type | Required | Description | |---|---|---|---| | `project_id` | string | Yes | Project ID | | `expense_category_id` | string | Yes | Expense category ID | | `spent_date` | string | Yes | Date of expense (YYYY-MM-DD) | | `total_cost` | number | Yes* | Total cost amount | | `units` | number | No | Quantity (e.g., tokens in thousands) | | `unit_price` | number | No | Price per unit | | `notes` | string | No | Description | | `source` | string | No | `web`, `cli`, `api`, `agent` | | `metadata` | object | No | Agent context JSON (max 4KB) | *If `units` and `unit_price` are provided, `total_cost` is calculated automatically. ### Example: Log LLM Token Cost ```bash curl -X POST https://api.keito.ai/v1/expenses \ -H "Authorization: Bearer keito_sk_abc123" \ -H "Content-Type: application/json" \ -d '{ "project_id": "prj_abc", "expense_category_id": "cat_llm_usage", "spent_date": "2026-03-06", "units": 45, "notes": "claude-opus-4-6: 30k input + 15k output tokens", "source": "agent", "metadata": { "agent_id": "review-bot-01", "session_id": "550e8400-e29b-41d4-a716-446655440000", "model": "claude-opus-4-6", "input_tokens": 30000, "output_tokens": 15000 } }' ``` ### Example Response ```json { "id": "exp_xyz789", "project_id": "prj_abc", "expense_category_id": "cat_llm_usage", "spent_date": "2026-03-06", "units": 45, "unit_price": 0.003, "total_cost": 0.135, "notes": "claude-opus-4-6: 30k input + 15k output tokens", "source": "agent", "metadata": { "agent_id": "review-bot-01", "session_id": "550e8400-e29b-41d4-a716-446655440000", "model": "claude-opus-4-6", "input_tokens": 30000, "output_tokens": 15000 }, "created_at": "2026-03-06T14:35:00Z" } ``` ## List Expenses `GET /v1/expenses` Returns a paginated list of expenses. Supports the same source, project, user, and date filters as time entries. ### Example: List All LLM Expenses for a Project ```bash curl "https://api.keito.ai/v1/expenses?source=agent&project_id=prj_abc&from=2026-03-01" \ -H "Authorization: Bearer keito_sk_abc123" ``` --- ## Projects API source: https://keito.ai/docs/api-reference/projects # Projects API ## List Projects `GET /v1/projects` Returns a paginated list of projects the authenticated user is assigned to. ### Query Parameters | Parameter | Type | Description | |---|---|---| | `cursor` | string | Pagination cursor | | `limit` | number | Results per page (default 50, max 200) | ### Example Request ```bash curl "https://api.keito.ai/v1/projects" \ -H "Authorization: Bearer keito_sk_abc123" ``` ### Example Response ```json { "data": [ { "id": "prj_abc", "name": "Acme Website Redesign", "client_id": "cli_xyz", "billing_type": "time_and_materials", "is_billable": true, "budget_hours": 200, "budget_amount": 30000, "is_active": true, "created_at": "2026-01-15T10:00:00Z" } ], "next_cursor": null, "has_more": false } ``` ## Get a Project `GET /v1/projects/:id` Returns a single project by ID. ### Example Request ```bash curl "https://api.keito.ai/v1/projects/prj_abc" \ -H "Authorization: Bearer keito_sk_abc123" ``` ### Project Fields | Field | Type | Description | |---|---|---| | `id` | string | Unique identifier | | `name` | string | Project name | | `client_id` | string | Associated client | | `billing_type` | string | `time_and_materials`, `fixed_fee`, `non_billable` | | `is_billable` | boolean | Whether time is billable by default | | `budget_hours` | number | Hour budget (if set) | | `budget_amount` | number | Monetary budget (if set) | | `is_active` | boolean | Whether the project is active | | `created_at` | string | ISO timestamp | --- ## Users API source: https://keito.ai/docs/api-reference/users # Users API ## List Users `GET /v1/users` Returns a paginated list of users in the workspace. ### Query Parameters | Parameter | Type | Description | |---|---|---| | `user_type` | string | Filter by type: `human` or `agent` | | `is_active` | boolean | Filter by active status | | `cursor` | string | Pagination cursor | | `limit` | number | Results per page (default 50, max 200) | ### Example: List All Agent Users ```bash curl "https://api.keito.ai/v1/users?user_type=agent" \ -H "Authorization: Bearer keito_sk_abc123" ``` ### Example Response ```json { "data": [ { "id": "usr_agent_01", "name": "review-bot-01", "email": "review-bot-01@agents.keito.ai", "user_type": "agent", "is_active": true, "billable_rate": 75.00, "cost_rate": 5.00, "created_at": "2026-02-01T10:00:00Z" } ], "next_cursor": null, "has_more": false } ``` ## Get a User `GET /v1/users/:id` Returns a single user by ID. ### User Fields | Field | Type | Description | |---|---|---| | `id` | string | Unique identifier | | `name` | string | Display name | | `email` | string | Email address | | `user_type` | string | `human` or `agent` | | `is_active` | boolean | Whether the user is active | | `billable_rate` | number | Billable rate per hour | | `cost_rate` | number | Internal cost rate per hour | | `created_at` | string | ISO timestamp | --- ## Error Handling source: https://keito.ai/docs/api-reference/error-handling # Error Handling All error responses use a consistent format: ```json { "error": { "code": "validation_error", "message": "project_id is required", "field": "project_id" } } ``` ## HTTP Status Codes | Code | Meaning | Common Cause | |---|---|---| | 400 | Bad Request | Missing required field, invalid value | | 401 | Unauthorized | Missing or invalid API key | | 403 | Forbidden | User not assigned to project | | 404 | Not Found | Resource doesn't exist | | 409 | Conflict | Duplicate entry (if idempotency used) | | 422 | Unprocessable Entity | Valid JSON but business rule violated | | 429 | Too Many Requests | Rate limit exceeded | | 500 | Internal Server Error | Server-side error | ## Error Codes | Code | Description | |---|---| | `validation_error` | Request body failed validation | | `authentication_error` | API key missing or invalid | | `authorization_error` | Insufficient permissions | | `not_found` | Resource not found | | `rate_limit_exceeded` | Too many requests | | `metadata_too_large` | Metadata exceeds 4KB limit | | `project_not_assigned` | User not assigned to the target project | ## Retry Strategy For `429` responses, read the `Retry-After` header (in seconds) and wait before retrying. For `5xx` responses, use exponential backoff starting at 1 second. Both SDKs handle retries automatically with configurable limits: ```typescript // Node SDK const keito = new Keito({ apiKey: process.env.KEITO_API_KEY, maxRetries: 3, retryDelay: 1000, }); ``` ```python # Python SDK client = Keito( api_key=os.environ["KEITO_API_KEY"], max_retries=3, retry_delay=1.0, ) ``` --- ## CLI: Getting Started source: https://keito.ai/docs/cli/getting-started # CLI: Getting Started The Keito CLI lets you track time and log expenses from your terminal. It's designed for developers running agents locally or in CI pipelines. ## Install ```bash # macOS (Homebrew) brew install keito-io/tap/keito # npm (cross-platform) npm install -g @keito/cli # From source cargo install keito-cli ``` ## Authenticate ```bash keito auth login ``` This opens a browser window. Log in, authorise the CLI, and you're set. Your credentials are stored securely in your system keychain. Alternatively, set environment variables: ```bash export KEITO_API_KEY="keito_sk_abc123..." export KEITO_ACCOUNT_ID="acc_xyz789" ``` ## Verify ```bash keito whoami ``` Output: ``` ✓ Authenticated as review-bot-01 Workspace: Acme Consulting Account: acc_xyz789 ``` ## Quick Start ```bash # Start a timer on a project keito time start --project acme --task development # Stop the timer with notes keito time stop --notes "Fixed authentication bug" # Log a completed block of time keito time log --project acme --task development \ --hours 1.5 --notes "Implemented OAuth flow" # Log an LLM expense keito expense log --project acme --quantity 45 \ --notes "claude-opus-4-6: 30k input + 15k output tokens" ``` ## Next Steps - [Tracking Time](/docs/cli/tracking-time) — detailed time tracking commands - [Agent Mode](/docs/cli/agent-mode) — auto-detection for AI agent environments - [Logging Expenses](/docs/cli/logging-expenses) — expense tracking commands --- ## CLI: Tracking Time source: https://keito.ai/docs/cli/tracking-time # CLI: Tracking Time The CLI supports two time tracking approaches: running timers and manual time logging. ## Starting a Timer ```bash keito time start --project acme --task development ``` This creates a running time entry. The timer ticks until you stop it. ### Options | Flag | Description | Required | |---|---|---| | `--project` | Project slug or ID | Yes | | `--task` | Task slug or ID | No | | `--notes` | Initial notes | No | | `--billable` | Mark as billable (default from project) | No | ## Stopping a Timer ```bash keito time stop --notes "Completed code review for PR #42" ``` This stops the running timer, calculates the duration, and saves the entry. ### Options | Flag | Description | |---|---| | `--notes` | Add or update notes | | `--round` | Round to nearest increment (e.g., `15m`, `6m`) | | `--discard` | Delete the entry instead of saving | ## Manual Time Logging For work that's already completed: ```bash keito time log --project acme --task development \ --hours 1.5 \ --date 2026-03-06 \ --notes "Implemented OAuth flow" ``` ### Options | Flag | Description | Required | |---|---|---| | `--project` | Project slug or ID | Yes | | `--task` | Task slug or ID | No | | `--hours` | Duration in decimal hours | Yes | | `--date` | Date (YYYY-MM-DD, default today) | No | | `--notes` | Description of work | No | | `--billable` | Mark as billable | No | ## Listing Time Entries ```bash # Today's entries keito time list # Entries for a specific date keito time list --date 2026-03-06 # Entries for a project keito time list --project acme ``` ## Check Running Timer ```bash keito time status ``` Output: ``` ⏱ Timer running: 1h 23m Project: Acme Consulting Task: Development Started: 2026-03-06 09:15 ``` --- ## CLI: Agent Mode source: https://keito.ai/docs/cli/agent-mode # CLI: Agent Mode When running inside an AI agent environment, the CLI automatically detects the agent context and sets the `source` field to `"agent"`. ## Auto-Detection The CLI checks for these environment variables: | Variable | Agent Platform | Sets agent_type to | |---|---|---| | `CLAUDE_SESSION_ID` | Claude Code | `claude-code` | | `CODEX_SESSION_ID` | Codex | `codex` | | `OPENCLAW_AGENT_ID` | OpenClaw | `openclaw` | | `CURSOR_AGENT` | Cursor | `cursor` | When detected, the CLI automatically: 1. Sets `source: "agent"` on all entries. 2. Populates `metadata.agent_type` from the environment. 3. Uses the session ID as `metadata.session_id`. ### Zero-Config Example (inside Claude Code) ```bash # These are auto-detected — no flags needed keito time start --project acme --task development # ... agent works ... keito time stop --notes "Implemented OAuth flow" keito expense log --project acme --quantity 45 \ --notes "claude-opus-4-6: 30k input + 15k output tokens" ``` The resulting time entry: ```json { "source": "agent", "metadata": { "agent_type": "claude-code", "session_id": "auto-detected-from-env", "agent_id": "review-bot-01" } } ``` ## Explicit Mode Override auto-detection with flags: ```bash keito time start --project acme --task development \ --source agent \ --agent-id my-custom-bot \ --session-id "$(uuidgen)" ``` ## Discarding Sessions If an agent session fails and you want to discard the timer: ```bash keito time stop --discard ``` This deletes the running time entry instead of saving it. ## Paired Workflow The recommended pattern for agent work is to always pair time and expense logging: ```bash # Start keito time start --project acme --task development # ... agent does work ... # Stop + log expense in one pipeline keito time stop --notes "Reviewed 5 pull requests" && \ keito expense log --project acme --quantity 62 \ --notes "claude-opus-4-6: 42k input + 20k output tokens" ``` --- ## CLI: Logging Expenses source: https://keito.ai/docs/cli/logging-expenses # CLI: Logging Expenses Log project expenses directly from your terminal. ## Basic Expense Logging ```bash keito expense log --project acme \ --category "LLM Usage" \ --quantity 45 \ --notes "claude-opus-4-6: 30k input + 15k output tokens" ``` ### Options | Flag | Description | Required | |---|---|---| | `--project` | Project slug or ID | Yes | | `--category` | Expense category name or ID | No (defaults to "LLM Usage" in agent mode) | | `--quantity` | Number of units | Yes | | `--total` | Total cost (alternative to quantity × unit price) | No | | `--notes` | Description | No | | `--date` | Date (YYYY-MM-DD, default today) | No | ## LLM Usage Shorthand In agent mode, the CLI defaults to the "LLM Usage" category: ```bash # These are equivalent in agent mode keito expense log --project acme --quantity 45 keito expense log --project acme --category "LLM Usage" --quantity 45 ``` ## Listing Expenses ```bash # Today's expenses keito expense list # Filter by project keito expense list --project acme # Filter by source keito expense list --source agent ``` --- ## CLI: Configuration source: https://keito.ai/docs/cli/configuration # CLI: Configuration The Keito CLI can be configured via environment variables, a config file, or command-line flags. ## Precedence Configuration is resolved in this order (highest priority first): 1. Command-line flags (e.g., `--project acme`) 2. Environment variables (e.g., `KEITO_PROJECT`) 3. Config file (`~/.keito/config.toml`) 4. Defaults ## Environment Variables | Variable | Description | |---|---| | `KEITO_API_KEY` | API key for authentication | | `KEITO_ACCOUNT_ID` | Your workspace/account ID | | `KEITO_PROJECT` | Default project slug | | `KEITO_TASK` | Default task slug | | `KEITO_SOURCE` | Default source value | ## Config File The CLI reads from `~/.keito/config.toml`: ```toml [auth] api_key = "keito_sk_abc123..." account_id = "acc_xyz789" [defaults] project = "acme" task = "development" source = "cli" [agent] agent_id = "review-bot-01" ``` Create the config file: ```bash keito config init ``` ## Project-Level Config Place a `.keito.toml` file in your project root to set per-project defaults: ```toml [defaults] project = "acme-website" task = "development" ``` This is useful for monorepos or teams where each repository maps to a specific Keito project. ## Setting Defaults ```bash # Set default project keito config set defaults.project acme # Set default task keito config set defaults.task development # View current config keito config list ``` --- ## CLI: CI Integration source: https://keito.ai/docs/cli/ci-integration # CLI: CI Integration Use the Keito CLI in GitHub Actions, GitLab CI, or any CI environment to track agent work that happens in automated pipelines. ## GitHub Actions ```yaml name: Agent Code Review on: [pull_request] jobs: review: runs-on: ubuntu-latest env: KEITO_API_KEY: ${{ secrets.KEITO_API_KEY }} KEITO_ACCOUNT_ID: ${{ secrets.KEITO_ACCOUNT_ID }} steps: - uses: actions/checkout@v4 - name: Install Keito CLI run: npm install -g @keito/cli - name: Start tracking run: keito time start --project ${{ vars.PROJECT_SLUG }} --task code-review --source agent --agent-id "gh-actions-reviewer" - name: Run AI code review run: ./scripts/ai-review.sh - name: Stop tracking and log cost if: always() run: | keito time stop --notes "AI code review for PR #${{ github.event.number }}" keito expense log --project ${{ vars.PROJECT_SLUG }} --quantity $TOKEN_COUNT \ --notes "Code review tokens for PR #${{ github.event.number }}" ``` ## GitLab CI ```yaml ai-review: stage: review variables: KEITO_API_KEY: $KEITO_API_KEY KEITO_ACCOUNT_ID: $KEITO_ACCOUNT_ID before_script: - npm install -g @keito/cli - keito time start --project myproject --task code-review --source agent script: - ./scripts/ai-review.sh after_script: - keito time stop --notes "AI review for $CI_COMMIT_SHORT_SHA" - keito expense log --project myproject --quantity $TOKEN_COUNT ``` ## Environment Variables Reference | Variable | Required | Description | |---|---|---| | `KEITO_API_KEY` | Yes | API key for authentication | | `KEITO_ACCOUNT_ID` | Yes | Your workspace ID | ## Tips - Use `if: always()` (GitHub Actions) or `after_script` (GitLab) to ensure tracking stops even if the agent step fails. - Store `KEITO_API_KEY` in your CI secrets — never commit it to source control. - Use a dedicated agent user for CI pipelines so the work is clearly attributed. - Consider logging the PR number or commit SHA in the notes for traceability. --- ## CLI: Command Reference source: https://keito.ai/docs/cli/command-reference # CLI: Command Reference ## Authentication | Command | Description | |---|---| | `keito auth login` | Authenticate via browser | | `keito auth logout` | Clear stored credentials | | `keito whoami` | Show current user and workspace | ## Time Tracking | Command | Description | |---|---| | `keito time start` | Start a running timer | | `keito time stop` | Stop the running timer | | `keito time log` | Log a completed block of time | | `keito time list` | List time entries | | `keito time status` | Show running timer status | | `keito time delete ` | Delete a time entry | ### keito time start ``` keito time start [flags] Flags: --project Project slug or ID (required) --task Task slug or ID --notes Initial notes --billable Mark as billable --source Source value (web, cli, api, agent) --agent-id Agent identifier (sets source to agent) --session-id Session UUID for correlation ``` ### keito time stop ``` keito time stop [flags] Flags: --notes Add or update notes --round Round to nearest increment (e.g., 15m, 6m) --discard Delete the entry instead of saving ``` ### keito time log ``` keito time log [flags] Flags: --project Project slug or ID (required) --task Task slug or ID --hours Duration in decimal hours (required) --date Date YYYY-MM-DD (default: today) --notes Description of work --billable Mark as billable --source Source value --agent-id Agent identifier --session-id Session UUID ``` ## Expenses | Command | Description | |---|---| | `keito expense log` | Log an expense | | `keito expense list` | List expenses | ### keito expense log ``` keito expense log [flags] Flags: --project Project slug or ID (required) --category Expense category name or ID --quantity Number of units (required) --total Total cost (alternative to quantity) --notes Description --date Date YYYY-MM-DD (default: today) --source Source value ``` ## Configuration | Command | Description | |---|---| | `keito config init` | Create config file | | `keito config set ` | Set a config value | | `keito config list` | Show current configuration | ## Global Flags | Flag | Description | |---|---| | `--help` | Show help for any command | | `--version` | Show CLI version | | `--json` | Output in JSON format | | `--quiet` | Suppress non-essential output | --- ## Node SDK: Getting Started source: https://keito.ai/docs/node-sdk/getting-started # Node SDK: Getting Started The Keito Node SDK (`@keito/sdk`) provides a typed client for TypeScript and JavaScript applications. ## Install ```bash npm install @keito/sdk # or yarn add @keito/sdk # or pnpm add @keito/sdk ``` ## Initialise ```typescript import { Keito } from '@keito/sdk'; const keito = new Keito({ apiKey: process.env.KEITO_API_KEY, accountId: process.env.KEITO_ACCOUNT_ID, }); ``` The client reads `KEITO_API_KEY` and `KEITO_ACCOUNT_ID` from environment variables by default, so you can also write: ```typescript const keito = new Keito(); ``` ## Configuration Options | Option | Type | Default | Description | |---|---|---|---| | `apiKey` | string | env | Your API key | | `accountId` | string | env | Your workspace ID | | `baseUrl` | string | `https://api.keito.ai/v1` | API base URL | | `maxRetries` | number | 2 | Max retry attempts on failure | | `timeout` | number | 30000 | Request timeout in ms | ## Your First Request ```typescript // List all projects const projects = await keito.projects.list(); for (const project of projects.data) { console.log(`${project.name} (${project.id})`); } ``` ## Next Steps - [Time Entries](/docs/node-sdk/time-entries) — create, list, and manage time entries - [Expenses](/docs/node-sdk/expenses) — log LLM token costs - [Agent Integration](/docs/node-sdk/agent-integration) — the session pattern for agent workflows --- ## Node SDK: Time Entries source: https://keito.ai/docs/node-sdk/time-entries # Node SDK: Time Entries ## Create a Time Entry ```typescript const entry = await keito.timeEntries.create({ project_id: 'prj_abc', task_id: 'tsk_001', spent_date: '2026-03-06', hours: 1.5, notes: 'Refactored authentication module', source: 'agent', metadata: { agent_id: 'review-bot-01', agent_type: 'claude-code', session_id: crypto.randomUUID(), model: 'claude-opus-4-6', }, }); ``` ## Start a Running Timer ```typescript const timer = await keito.timeEntries.create({ project_id: 'prj_abc', task_id: 'tsk_001', spent_date: new Date().toISOString().split('T')[0], hours: 0, is_running: true, source: 'agent', metadata: { agent_id: 'review-bot-01', session_id: crypto.randomUUID(), }, }); ``` ## Stop a Timer ```typescript await keito.timeEntries.update(timer.id, { hours: 1.5, is_running: false, notes: 'Completed code review', }); ``` ## List Time Entries ```typescript // All agent entries for a project this week const entries = await keito.timeEntries.list({ source: 'agent', project_id: 'prj_abc', from: '2026-03-01', to: '2026-03-06', }); for (const entry of entries.data) { console.log(`${entry.spent_date}: ${entry.hours}h — ${entry.notes}`); } ``` ## Pagination ```typescript let cursor: string | undefined; do { const page = await keito.timeEntries.list({ project_id: 'prj_abc', cursor, limit: 50, }); for (const entry of page.data) { console.log(entry.id); } cursor = page.next_cursor ?? undefined; } while (cursor); ``` ## Delete a Time Entry ```typescript await keito.timeEntries.delete('te_abc123'); ``` --- ## Node SDK: Expenses source: https://keito.ai/docs/node-sdk/expenses # Node SDK: Expenses ## Create an Expense ```typescript await keito.expenses.create({ project_id: 'prj_abc', expense_category_id: 'cat_llm_usage', spent_date: '2026-03-06', units: 45, notes: 'claude-opus-4-6: 30k input + 15k output tokens', source: 'agent', metadata: { agent_id: 'review-bot-01', session_id: '550e8400-e29b-41d4-a716-446655440000', model: 'claude-opus-4-6', input_tokens: 30000, output_tokens: 15000, }, }); ``` ## List Expenses ```typescript const expenses = await keito.expenses.list({ source: 'agent', project_id: 'prj_abc', from: '2026-03-01', }); for (const expense of expenses.data) { console.log(`${expense.spent_date}: ${expense.units} units — ${expense.notes}`); } ``` ## Get a Single Expense ```typescript const expense = await keito.expenses.get('exp_xyz789'); console.log(`Total cost: ${expense.total_cost}`); ``` --- ## Node SDK: Agent Integration source: https://keito.ai/docs/node-sdk/agent-integration # Node SDK: Agent Integration This guide covers patterns for integrating Keito with AI agents in TypeScript/JavaScript applications. ## The Agent Session Pattern The most common pattern: start a timer when the agent begins work, stop it when done, and log an LLM expense for token costs. ```typescript import { Keito } from '@keito/sdk'; const keito = new Keito(); // 1. Start a running timer const entry = await keito.timeEntries.create({ project_id: 'prj_abc', task_id: 'tsk_001', spent_date: new Date().toISOString().split('T')[0], hours: 0, is_running: true, source: 'agent', metadata: { agent_id: 'review-bot-01', agent_type: 'claude-code', session_id: crypto.randomUUID(), model: 'claude-opus-4-6', }, }); // 2. Agent does work... const result = await myAgent.run('Review the authentication module'); // 3. Stop the timer await keito.timeEntries.update(entry.id, { hours: 1.5, is_running: false, notes: 'Reviewed and refactored auth module', }); // 4. Log LLM cost await keito.expenses.create({ project_id: 'prj_abc', expense_category_id: 'cat_llm_usage', spent_date: new Date().toISOString().split('T')[0], units: 40, notes: 'claude-opus-4-6: 25k input + 15k output', source: 'agent', metadata: { agent_id: 'review-bot-01', session_id: entry.metadata.session_id, model: 'claude-opus-4-6', input_tokens: 25000, output_tokens: 15000, }, }); ``` ## The Wrapper Helper For repeated agent calls, use a wrapper that handles tracking automatically: ```typescript async function trackAgentWork( keito: Keito, config: { projectId: string; taskId: string; agentId: string; model: string; }, work: () => Promise<{ result: T; inputTokens: number; outputTokens: number; }>, ): Promise { const sessionId = crypto.randomUUID(); const today = new Date().toISOString().split('T')[0]; const entry = await keito.timeEntries.create({ project_id: config.projectId, task_id: config.taskId, spent_date: today, hours: 0, is_running: true, source: 'agent', metadata: { agent_id: config.agentId, session_id: sessionId, model: config.model, }, }); const startTime = Date.now(); try { const { result, inputTokens, outputTokens } = await work(); const hours = (Date.now() - startTime) / 3_600_000; await keito.timeEntries.update(entry.id, { hours: Math.round(hours * 100) / 100, is_running: false, }); const totalTokensK = (inputTokens + outputTokens) / 1000; await keito.expenses.create({ project_id: config.projectId, expense_category_id: 'cat_llm_usage', spent_date: today, units: totalTokensK, source: 'agent', metadata: { agent_id: config.agentId, session_id: sessionId, model: config.model, input_tokens: inputTokens, output_tokens: outputTokens, }, }); return result; } catch (err) { await keito.timeEntries.delete(entry.id); throw err; } } ``` ## Metadata Best Practices - Always include `agent_id` and `session_id`. - Use `session_id` to correlate time entries with their LLM expenses. - Keep metadata under 4KB. - Include `model` for cost analysis across different LLMs. - Don't store sensitive data in metadata — it's visible in reports. --- ## Node SDK: Filtering & Pagination source: https://keito.ai/docs/node-sdk/filtering # Node SDK: Filtering & Pagination ## Filtering Time Entries All list methods accept filter parameters: ```typescript const entries = await keito.timeEntries.list({ source: 'agent', project_id: 'prj_abc', user_id: 'usr_agent_01', from: '2026-03-01', to: '2026-03-31', is_running: false, limit: 100, }); ``` ### Available Filters | Parameter | Type | Description | |---|---|---| | `source` | string | Filter by source: `web`, `cli`, `api`, `agent` | | `project_id` | string | Filter by project | | `user_id` | string | Filter by user | | `from` | string | Start date (inclusive, YYYY-MM-DD) | | `to` | string | End date (inclusive, YYYY-MM-DD) | | `is_running` | boolean | Filter running timers only | | `limit` | number | Results per page (default 50, max 200) | | `cursor` | string | Pagination cursor | ## Pagination The SDK uses cursor-based pagination. Each response includes `next_cursor` when more results exist: ```typescript let cursor: string | undefined; const allEntries = []; do { const page = await keito.timeEntries.list({ project_id: 'prj_abc', cursor, limit: 200, }); allEntries.push(...page.data); cursor = page.next_cursor ?? undefined; } while (cursor); console.log(`Total entries: ${allEntries.length}`); ``` ## Filtering Expenses Expenses support the same filter parameters: ```typescript const llmExpenses = await keito.expenses.list({ source: 'agent', project_id: 'prj_abc', from: '2026-03-01', }); const totalCost = llmExpenses.data.reduce( (sum, exp) => sum + exp.total_cost, 0 ); console.log(`Total LLM cost: £${totalCost.toFixed(2)}`); ``` ## Combining Filters Filters are combined with AND logic: ```typescript // Agent entries for a specific project in March const entries = await keito.timeEntries.list({ source: 'agent', project_id: 'prj_abc', from: '2026-03-01', to: '2026-03-31', }); ``` --- ## Node SDK: Error Handling source: https://keito.ai/docs/node-sdk/error-handling # Node SDK: Error Handling ## Error Types The SDK throws typed errors for different failure scenarios: ```typescript import { Keito, KeitoError, ValidationError, AuthenticationError, RateLimitError } from '@keito/sdk'; try { await keito.timeEntries.create({ /* ... */ }); } catch (err) { if (err instanceof ValidationError) { console.error(`Validation failed: ${err.message} (field: ${err.field})`); } else if (err instanceof AuthenticationError) { console.error('Invalid API key'); } else if (err instanceof RateLimitError) { console.error(`Rate limited. Retry after ${err.retryAfter}s`); } else if (err instanceof KeitoError) { console.error(`API error: ${err.code} — ${err.message}`); } } ``` ## Error Properties All `KeitoError` instances include: | Property | Type | Description | |---|---|---| | `code` | string | Error code (e.g., `validation_error`) | | `message` | string | Human-readable description | | `status` | number | HTTP status code | | `field` | string | Field that caused the error (validation only) | ## Automatic Retries The SDK automatically retries on `429` (rate limit) and `5xx` (server error) responses: ```typescript const keito = new Keito({ maxRetries: 3, // default: 2 retryDelay: 1000, // default: 1000ms (exponential backoff) }); ``` Retries use exponential backoff. For `429` responses, the SDK respects the `Retry-After` header. ## Disabling Retries ```typescript const keito = new Keito({ maxRetries: 0, }); ``` ## Handling Specific Scenarios ### Agent Session Cleanup If an agent fails mid-session, clean up the running timer: ```typescript const entry = await keito.timeEntries.create({ project_id: 'prj_abc', hours: 0, is_running: true, source: 'agent', /* ... */ }); try { await myAgent.run(); } catch (err) { // Discard the running timer await keito.timeEntries.delete(entry.id); throw err; } ``` ### Project Not Assigned ```typescript try { await keito.timeEntries.create({ project_id: 'prj_not_assigned', /* ... */ }); } catch (err) { if (err instanceof KeitoError && err.code === 'project_not_assigned') { console.error('Agent user is not assigned to this project'); } } ``` --- ## Node SDK: TypeScript Types source: https://keito.ai/docs/node-sdk/typescript-types # Node SDK: TypeScript Types The SDK exports full TypeScript types for all API objects. ## Importing Types ```typescript import type { TimeEntry, CreateTimeEntryInput, UpdateTimeEntryInput, Expense, CreateExpenseInput, Project, User, PaginatedResponse, ListTimeEntriesParams, ListExpensesParams, } from '@keito/sdk'; ``` ## TimeEntry ```typescript interface TimeEntry { id: string; project_id: string; task_id: string | null; user_id: string; spent_date: string; hours: number; notes: string | null; is_running: boolean; is_billable: boolean; source: 'web' | 'cli' | 'api' | 'agent'; metadata: Record | null; created_at: string; updated_at: string; } ``` ## CreateTimeEntryInput ```typescript interface CreateTimeEntryInput { project_id: string; spent_date: string; hours?: number; task_id?: string; notes?: string; is_running?: boolean; is_billable?: boolean; source?: 'web' | 'cli' | 'api' | 'agent'; metadata?: Record; } ``` ## Expense ```typescript interface Expense { id: string; project_id: string; expense_category_id: string; spent_date: string; units: number | null; unit_price: number | null; total_cost: number; notes: string | null; source: 'web' | 'cli' | 'api' | 'agent'; metadata: Record | null; created_at: string; } ``` ## Project ```typescript interface Project { id: string; name: string; client_id: string; billing_type: 'time_and_materials' | 'fixed_fee' | 'non_billable'; is_billable: boolean; budget_hours: number | null; budget_amount: number | null; is_active: boolean; created_at: string; } ``` ## User ```typescript interface User { id: string; name: string; email: string; user_type: 'human' | 'agent'; is_active: boolean; billable_rate: number; cost_rate: number; created_at: string; } ``` ## PaginatedResponse ```typescript interface PaginatedResponse { data: T[]; next_cursor: string | null; has_more: boolean; } ``` ## Source Type ```typescript type Source = 'web' | 'cli' | 'api' | 'agent'; ``` --- ## Python SDK: Getting Started source: https://keito.ai/docs/python-sdk/getting-started # Python SDK: Getting Started The Keito Python SDK (`keito-python`) provides a client for Python applications, agent frameworks, and notebooks. ## Install ```bash pip install keito-python # or poetry add keito-python ``` ## Initialise ```python import os from keito import Keito client = Keito( api_key=os.environ["KEITO_API_KEY"], account_id=os.environ["KEITO_ACCOUNT_ID"], ) ``` Like the Node SDK, the Python client reads from environment variables by default: ```python client = Keito() # reads KEITO_API_KEY and KEITO_ACCOUNT_ID from env ``` ## Configuration Options | Option | Type | Default | Description | |---|---|---|---| | `api_key` | str | env | Your API key | | `account_id` | str | env | Your workspace ID | | `base_url` | str | `https://api.keito.ai/v1` | API base URL | | `max_retries` | int | 2 | Max retry attempts on failure | | `timeout` | float | 30.0 | Request timeout in seconds | ## Your First Request ```python # List all projects projects = client.projects.list() for project in projects.data: print(f"{project.name} ({project.id})") ``` ## Next Steps - [Time Entries](/docs/python-sdk/time-entries) — create, list, and manage time entries - [Expenses](/docs/python-sdk/expenses) — log LLM token costs - [Agent Integration](/docs/python-sdk/agent-integration) — context manager and decorator patterns --- ## Python SDK: Time Entries source: https://keito.ai/docs/python-sdk/time-entries # Python SDK: Time Entries ## Create a Time Entry ```python entry = client.time_entries.create( project_id="prj_abc", task_id="tsk_001", spent_date="2026-03-06", hours=1.5, notes="Refactored authentication module", source="agent", metadata={ "agent_id": "review-bot-01", "agent_type": "claude-code", "session_id": "550e8400-e29b-41d4-a716-446655440000", "model": "claude-opus-4-6", }, ) ``` ## Start a Running Timer ```python from datetime import date timer = client.time_entries.create( project_id="prj_abc", task_id="tsk_001", spent_date=str(date.today()), hours=0, is_running=True, source="agent", metadata={ "agent_id": "review-bot-01", "session_id": "550e8400-e29b-41d4-a716-446655440000", }, ) ``` ## Stop a Timer ```python client.time_entries.update( timer.id, hours=1.5, is_running=False, notes="Completed code review", ) ``` ## List Time Entries ```python # All agent entries for a project this week entries = client.time_entries.list( source="agent", project_id="prj_abc", from_date="2026-03-01", to_date="2026-03-06", ) for entry in entries.data: print(f"{entry.spent_date}: {entry.hours}h — {entry.notes}") ``` ## Delete a Time Entry ```python client.time_entries.delete("te_abc123") ``` --- ## Python SDK: Expenses source: https://keito.ai/docs/python-sdk/expenses # Python SDK: Expenses ## Create an Expense ```python client.expenses.create( project_id="prj_abc", expense_category_id="cat_llm_usage", spent_date="2026-03-06", units=45, notes="claude-opus-4-6: 30k input + 15k output tokens", source="agent", metadata={ "agent_id": "review-bot-01", "session_id": "550e8400-e29b-41d4-a716-446655440000", "model": "claude-opus-4-6", "input_tokens": 30000, "output_tokens": 15000, }, ) ``` ## List Expenses ```python expenses = client.expenses.list( source="agent", project_id="prj_abc", from_date="2026-03-01", ) for expense in expenses.data: print(f"{expense.spent_date}: {expense.units} units — {expense.notes}") ``` ## Get a Single Expense ```python expense = client.expenses.get("exp_xyz789") print(f"Total cost: {expense.total_cost}") ``` --- ## Python SDK: Agent Integration source: https://keito.ai/docs/python-sdk/agent-integration # Python SDK: Agent Integration This guide covers Pythonic patterns for integrating Keito with AI agents. ## Context Manager Pattern The recommended approach — a `with` block that handles start, stop, and expense logging automatically: ```python from keito import Keito client = Keito() with client.track_agent_work( project_id="prj_abc", task_id="tsk_001", agent_id="research-bot-03", model="claude-opus-4-6", ) as session: # Your agent does work here result = agent.run("Analyse Q4 billing data") # Report token usage — logged as LLM expense on exit session.report_tokens(input=18000, output=6000) session.set_notes("Analysed Q4 billing trends and generated summary") # Time entry and LLM expense are created automatically print(f"Tracked {session.hours:.2f}h, {session.total_tokens_k:.1f}k tokens") ``` If an exception is raised inside the `with` block, the time entry is automatically discarded (deleted). ## Decorator Pattern For agent tool functions that should be tracked automatically: ```python from keito import track @track(project_id="prj_abc", task_id="tsk_001") async def review_pull_request(pr_url: str) -> str: """Agent reviews a PR — time and cost tracked automatically.""" response = await agent.review(pr_url) return response.summary ``` ## Async Client For high-throughput agent work, use the async client: ```python from keito import AsyncKeito client = AsyncKeito() async with client.track_agent_work( project_id="prj_abc", agent_id="batch-processor", model="claude-sonnet-4-6", ) as session: results = await asyncio.gather(*[ process_item(item) for item in items ]) session.report_tokens(input=total_input, output=total_output) ``` ## Metadata Best Practices - Always include `agent_id` and `session_id`. - Use `session_id` to correlate time entries with their LLM expenses. - Keep metadata under 4KB. - Include `model` for cost analysis across different LLMs. - Don't store sensitive data in metadata — it's visible in reports. --- ## Python SDK: LangChain Integration source: https://keito.ai/docs/python-sdk/langchain # Python SDK: LangChain Integration The Keito Python SDK includes a LangChain callback handler that automatically tracks time and token usage for every chain invocation. ## Setup ```python from keito import Keito from keito.integrations.langchain import KeitoCallbackHandler client = Keito() handler = KeitoCallbackHandler( client=client, project_id="prj_abc", task_id="tsk_001", agent_id="langchain-agent-01", ) ``` ## Usage Pass the handler as a callback to any LangChain chain or agent: ```python from langchain_anthropic import ChatAnthropic from langchain_core.prompts import ChatPromptTemplate llm = ChatAnthropic(model="claude-sonnet-4-6") prompt = ChatPromptTemplate.from_template("Review this code: {code}") chain = prompt | llm # Automatically tracks time and token usage result = chain.invoke( {"code": code_to_review}, config={"callbacks": [handler]}, ) ``` ## What Gets Tracked For each chain invocation, the handler creates: 1. **A time entry** with `source: "agent"` and duration based on wall-clock time. 2. **An LLM expense** with token counts from the LangChain usage metadata. Both entries share the same `session_id` in their metadata for correlation. ## Configuration | Option | Type | Default | Description | |---|---|---|---| | `auto_expense` | bool | True | Automatically log LLM expenses | | `min_duration` | float | 0.01 | Minimum hours to record (skip very short calls) | | `notes_template` | str | None | Template for entry notes | ## Multi-Step Agents For LangChain agents with multiple tool calls, each step is tracked individually: ```python from langchain.agents import AgentExecutor, create_tool_calling_agent agent = create_tool_calling_agent(llm, tools, prompt) executor = AgentExecutor(agent=agent, tools=tools) result = executor.invoke( {"input": "Analyse the Q4 sales data"}, config={"callbacks": [handler]}, ) ``` The handler creates one time entry for the full agent run and separate LLM expenses for each LLM call within the run. ## Related - [Agent Integration](/docs/python-sdk/agent-integration) — manual integration patterns - [CrewAI Integration](/docs/python-sdk/crewai) — CrewAI task callback --- ## Python SDK: CrewAI Integration source: https://keito.ai/docs/python-sdk/crewai # Python SDK: CrewAI Integration Track time and token costs for CrewAI agents using the Keito task callback. ## Setup ```python from keito import Keito from keito.integrations.crewai import KeitoTaskCallback client = Keito() callback = KeitoTaskCallback( client=client, project_id="prj_abc", ) ``` ## Usage ```python from crewai import Agent, Task, Crew researcher = Agent( role="Senior Researcher", goal="Find relevant information", backstory="An experienced research analyst", llm="claude-sonnet-4-6", ) task = Task( description="Research Q4 market trends", agent=researcher, callback=callback, ) crew = Crew(agents=[researcher], tasks=[task]) result = crew.kickoff() ``` Each task creates a time entry and LLM expense attributed to the agent that executed it. ## What Gets Tracked For each CrewAI task: 1. **A time entry** — duration measured from task start to completion, with `source: "agent"` and the agent's role as the notes. 2. **An LLM expense** — total token usage across all LLM calls within the task. ## Multi-Agent Crews When a crew has multiple agents, each task is attributed to the agent that executed it: ```python researcher = Agent(role="Researcher", llm="claude-sonnet-4-6") writer = Agent(role="Writer", llm="claude-sonnet-4-6") research_task = Task( description="Research AI trends", agent=researcher, callback=callback, ) writing_task = Task( description="Write report from research", agent=writer, callback=callback, context=[research_task], ) crew = Crew( agents=[researcher, writer], tasks=[research_task, writing_task], ) result = crew.kickoff() ``` This creates separate time entries and expenses for each task, making it clear how much time and cost each agent contributed. ## Configuration | Option | Type | Default | Description | |---|---|---|---| | `auto_expense` | bool | True | Automatically log LLM expenses | | `agent_id_from_role` | bool | True | Use agent role as `agent_id` in metadata | | `task_id` | str | None | Default task ID for all entries | ## Related - [LangChain Integration](/docs/python-sdk/langchain) — LangChain callback handler - [Agent Integration](/docs/python-sdk/agent-integration) — manual integration patterns --- ## Python SDK: Async Usage source: https://keito.ai/docs/python-sdk/async-usage # Python SDK: Async Usage For high-throughput agent work, use the async client to make non-blocking API calls. ## Setup ```python from keito import AsyncKeito client = AsyncKeito() ``` The `AsyncKeito` client accepts the same configuration options as the sync client. ## Basic Usage ```python import asyncio from keito import AsyncKeito async def main(): client = AsyncKeito() # Create a time entry entry = await client.time_entries.create( project_id="prj_abc", task_id="tsk_001", spent_date="2026-03-06", hours=1.5, notes="Async agent work", source="agent", ) # List entries entries = await client.time_entries.list( source="agent", project_id="prj_abc", ) for e in entries.data: print(f"{e.spent_date}: {e.hours}h") asyncio.run(main()) ``` ## Async Context Manager The `track_agent_work` context manager works with `async with`: ```python async with client.track_agent_work( project_id="prj_abc", agent_id="batch-processor", model="claude-sonnet-4-6", ) as session: results = await asyncio.gather(*[ process_item(item) for item in items ]) session.report_tokens(input=total_input, output=total_output) ``` ## Parallel Agent Work Process multiple agent tasks concurrently while tracking each one: ```python async def process_pr(client: AsyncKeito, pr_url: str): async with client.track_agent_work( project_id="prj_abc", task_id="tsk_code_review", agent_id="review-bot", model="claude-sonnet-4-6", ) as session: result = await review_agent.run(pr_url) session.report_tokens( input=result.input_tokens, output=result.output_tokens, ) session.set_notes(f"Reviewed {pr_url}") return result # Process 5 PRs in parallel results = await asyncio.gather(*[ process_pr(client, url) for url in pr_urls ]) ``` ## When to Use Async - **Batch processing** — processing multiple items concurrently - **Web applications** — FastAPI, Starlette, or other async frameworks - **Agent orchestration** — running multiple agents in parallel - **High-frequency logging** — many short agent tasks in sequence --- ## Python SDK: API Reference source: https://keito.ai/docs/python-sdk/reference # Python SDK: API Reference ## Client Classes ### Keito ```python from keito import Keito client = Keito( api_key: str = None, # defaults to KEITO_API_KEY env var account_id: str = None, # defaults to KEITO_ACCOUNT_ID env var base_url: str = "https://api.keito.ai/v1", max_retries: int = 2, timeout: float = 30.0, ) ``` ### AsyncKeito ```python from keito import AsyncKeito client = AsyncKeito( # Same parameters as Keito ) ``` ## Time Entries ### client.time_entries.create() ```python entry = client.time_entries.create( project_id: str, # required spent_date: str, # required, YYYY-MM-DD hours: float = 0, # required unless is_running=True task_id: str = None, notes: str = None, is_running: bool = False, is_billable: bool = None, # defaults from project source: str = "api", metadata: dict = None, # max 4KB ) -> TimeEntry ``` ### client.time_entries.list() ```python entries = client.time_entries.list( source: str = None, project_id: str = None, user_id: str = None, from_date: str = None, # YYYY-MM-DD to_date: str = None, # YYYY-MM-DD is_running: bool = None, cursor: str = None, limit: int = 50, # max 200 ) -> PaginatedResponse[TimeEntry] ``` ### client.time_entries.get() ```python entry = client.time_entries.get(id: str) -> TimeEntry ``` ### client.time_entries.update() ```python entry = client.time_entries.update( id: str, hours: float = None, notes: str = None, is_running: bool = None, is_billable: bool = None, task_id: str = None, metadata: dict = None, ) -> TimeEntry ``` ### client.time_entries.delete() ```python client.time_entries.delete(id: str) -> None ``` ## Expenses ### client.expenses.create() ```python expense = client.expenses.create( project_id: str, # required expense_category_id: str, # required spent_date: str, # required, YYYY-MM-DD total_cost: float = None, # auto-calculated if units + unit_price units: float = None, unit_price: float = None, notes: str = None, source: str = "api", metadata: dict = None, ) -> Expense ``` ### client.expenses.list() ```python expenses = client.expenses.list( source: str = None, project_id: str = None, user_id: str = None, from_date: str = None, to_date: str = None, cursor: str = None, limit: int = 50, ) -> PaginatedResponse[Expense] ``` ### client.expenses.get() ```python expense = client.expenses.get(id: str) -> Expense ``` ## Projects ### client.projects.list() ```python projects = client.projects.list( cursor: str = None, limit: int = 50, ) -> PaginatedResponse[Project] ``` ### client.projects.get() ```python project = client.projects.get(id: str) -> Project ``` ## Types ### TimeEntry | Field | Type | Description | |---|---|---| | `id` | str | Unique identifier | | `project_id` | str | Project ID | | `task_id` | str or None | Task ID | | `user_id` | str | User ID | | `spent_date` | str | Date (YYYY-MM-DD) | | `hours` | float | Duration in hours | | `notes` | str or None | Description | | `is_running` | bool | Timer active | | `is_billable` | bool | Billable status | | `source` | str | Origin | | `metadata` | dict or None | Agent context | | `created_at` | str | ISO timestamp | | `updated_at` | str | ISO timestamp | ### Expense | Field | Type | Description | |---|---|---| | `id` | str | Unique identifier | | `project_id` | str | Project ID | | `expense_category_id` | str | Category ID | | `spent_date` | str | Date (YYYY-MM-DD) | | `units` | float or None | Quantity | | `unit_price` | float or None | Price per unit | | `total_cost` | float | Total amount | | `notes` | str or None | Description | | `source` | str | Origin | | `metadata` | dict or None | Agent context | | `created_at` | str | ISO timestamp | ### PaginatedResponse | Field | Type | Description | |---|---|---| | `data` | list | Array of results | | `next_cursor` | str or None | Cursor for next page | | `has_more` | bool | Whether more results exist | --- ## Agent Integration Overview source: https://keito.ai/docs/agent-integration/overview # Agent Integration Overview This section explains the concepts behind agent tracking in Keito and provides guidance for integrating any AI agent — whether you're using the SDK, CLI, or building a custom integration. ## The Core Pattern Every agent integration follows the same three-step pattern: 1. **Start** — create a time entry when the agent begins work. 2. **Stop** — update the time entry when the agent finishes. 3. **Log cost** — create an LLM expense for token usage. ``` ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ │ Start Timer │────▶│ Agent Works │────▶│ Stop Timer │ │ POST entry │ │ ... │ │ PATCH entry │ │ is_running: │ │ │ │ hours: 1.5 │ │ true │ │ │ │ is_running: │ │ │ │ │ │ false │ └──────────────┘ └──────────────┘ └──────┬───────┘ │ ┌──────▼───────┐ │ Log Expense │ │ POST expense│ │ units: 45 │ └──────────────┘ ``` ## Session Correlation Use a shared `session_id` in the metadata of both the time entry and the expense to link them: ```json { "session_id": "550e8400-e29b-41d4-a716-446655440000", "agent_id": "review-bot-01" } ``` This lets you trace which token costs correspond to which work sessions in reports. ## Error Handling If the agent fails mid-session: - **CLI**: `keito time stop --discard` - **SDK**: Delete the time entry (`keito.timeEntries.delete(entry.id)`) - **Python context manager**: Automatic — discards on exception. Don't leave running timers from failed sessions. They'll appear as in-progress entries in the UI. ## Choosing an Integration Method | Scenario | Recommended Method | |---|---| | Running agents in the terminal | CLI | | Custom TypeScript/JS agent | Node SDK | | Python agent or notebook | Python SDK | | LangChain / CrewAI framework | Python SDK + integration | | CI/CD pipeline | CLI | | Non-JS/Python language | REST API | | Zapier / n8n / Make | REST API | ## Next Steps - [How Agent Billing Works](/docs/agent-integration/billing) — rates, margins, and invoicing - [Quickstart](/docs/developer/quickstart) — create your first agent time entry - [Node SDK Agent Integration](/docs/node-sdk/agent-integration) — TypeScript patterns - [Python SDK Agent Integration](/docs/python-sdk/agent-integration) — Python patterns --- ## How Agent Billing Works source: https://keito.ai/docs/agent-integration/billing # How Agent Billing Works This page explains how agent work flows from tracked time to client invoices. ## The Full Flow ``` Agent works → Time entry created → Manager approves → Invoice generated → Client pays ↓ LLM expense created → Included in invoice (optional) ``` ## Rate Structure Agent users have their own rates, separate from humans: | Component | Set Where | Example | |---|---|---| | Agent hourly rate | Team → Agent User → Billable Rate | £75/hr | | Agent cost rate | Team → Agent User → Cost Rate | £5/hr | | LLM unit price | Expense Categories → LLM Usage | £0.003/1k tokens | ## Margin Calculation **Revenue**: Agent hours × billable rate + LLM cost pass-through **Cost**: Agent hours × cost rate + actual LLM spend **Margin**: Revenue - Cost ### Example | Line Item | Revenue | Cost | Margin | |---|---|---|---| | 6h agent work @ £75/hr | £450 | £30 | £420 | | 180k tokens @ £0.003/k | £0.54 | £0.54 | £0.00 | | **Total** | **£450.54** | **£30.54** | **£420** | ## Tips for Clients - Be transparent about agent work on invoices. - Use separate line items for agent time and LLM costs. - Include a brief explanation the first time a client sees agent charges. - Consider a lower rate for agent work to reflect the reduced human effort. ## Related - [Invoicing Agent Work](/docs/invoicing/agent-work) — how agent entries appear on invoices - [Managing Agent Users](/docs/team-management/agent-users) — setting agent rates - [Cost Allocation Patterns](/docs/best-practices/cost-allocation) — advanced billing strategies --- ## Framework Guides source: https://keito.ai/docs/agent-integration/frameworks # Framework Guides Keito integrates with popular AI agent frameworks through the Python SDK. Each integration automatically tracks time and LLM token costs. ## Supported Frameworks | Framework | Integration | Documentation | |---|---|---| | LangChain | Callback handler | [LangChain Integration](/docs/python-sdk/langchain) | | CrewAI | Task callback | [CrewAI Integration](/docs/python-sdk/crewai) | | Custom Python | Context manager / decorator | [Python Agent Integration](/docs/python-sdk/agent-integration) | | Custom TypeScript | Wrapper function | [Node Agent Integration](/docs/node-sdk/agent-integration) | | CLI-based agents | Auto-detection | [CLI Agent Mode](/docs/cli/agent-mode) | ## How Framework Integrations Work All framework integrations follow the same underlying pattern: 1. **Hook into the framework's lifecycle** — callbacks, decorators, or context managers that fire when agent work starts and stops. 2. **Create a time entry** with `source: "agent"` when work begins. 3. **Capture token usage** from the framework's built-in tracking. 4. **Stop the timer and log an expense** when work completes. ## Choosing a Framework Integration - **LangChain** — best for chain and agent pipelines. The callback handler hooks into LangChain's callback system and tracks each invocation automatically. - **CrewAI** — best for multi-agent crews. The task callback tracks each task separately, attributing work to the specific agent that executed it. - **Custom** — best for bespoke agent implementations. Use the context manager (Python) or wrapper helper (Node) for full control. ## Building a Custom Integration If your framework isn't listed, use the REST API directly. The pattern is always the same: 1. `POST /v1/time-entries` with `is_running: true` when the agent starts. 2. `PATCH /v1/time-entries/:id` with `is_running: false` and final `hours` when done. 3. `POST /v1/expenses` with token counts. See [REST API Reference](/docs/api-reference/overview) for full endpoint documentation. --- ## Metadata Conventions source: https://keito.ai/docs/best-practices/metadata # Metadata Conventions Best practices for the `metadata` field on agent time entries and expenses. ## Recommended Fields | Field | Type | Required | Description | |---|---|---|---| | `agent_id` | string | Yes | Unique agent identifier | | `agent_type` | string | Yes | Platform: `claude-code`, `codex`, `cursor`, `langchain`, `crewai`, `custom` | | `session_id` | string | Yes | UUID linking time + expense | | `model` | string | Yes | LLM model name | | `input_tokens` | number | Expense | Input tokens consumed | | `output_tokens` | number | Expense | Output tokens consumed | | `pr_url` | string | Optional | Pull request URL (for code review agents) | | `task_description` | string | Optional | Human-readable task description | ## Size Limits The metadata field has a 4KB maximum. In practice, this is more than enough for agent context. If you're hitting the limit, you're likely storing data that belongs in notes or an external system. ## Naming Conventions - Use `snake_case` for all metadata keys. - Use consistent `agent_id` values across sessions (it's the agent's identity, not the session's). - Use consistent `agent_type` values (lowercase, hyphenated). ## What NOT to Store - Sensitive data (API keys, credentials, PII) - Full LLM responses or prompts - Binary data or base64-encoded content - Ephemeral data that changes every second ## Example: Well-Structured Metadata ### Time Entry ```json { "agent_id": "review-bot-01", "agent_type": "claude-code", "session_id": "550e8400-e29b-41d4-a716-446655440000", "model": "claude-opus-4-6", "pr_url": "https://github.com/acme/app/pull/42" } ``` ### LLM Expense ```json { "agent_id": "review-bot-01", "agent_type": "claude-code", "session_id": "550e8400-e29b-41d4-a716-446655440000", "model": "claude-opus-4-6", "input_tokens": 25000, "output_tokens": 15000 } ``` --- ## Cost Allocation Patterns source: https://keito.ai/docs/best-practices/cost-allocation # Cost Allocation Patterns How to allocate agent and LLM costs across projects and clients. ## Pattern 1: Pass-Through LLM costs are invoiced directly to clients at cost. The margin comes from the agent's billable rate. **Best for**: Clients who want full transparency. ### Setup 1. Set the agent billable rate to your desired margin. 2. Include LLM expenses on invoices as a separate line item. ### Example | Line Item | Amount | |---|---| | Agent work: 6h @ £75/hr | £450 | | LLM tokens: 180k @ £0.003/k | £0.54 | | **Total** | **£450.54** | ## Pattern 2: Bundled Rate Agent rate includes an estimated LLM cost. LLM expenses are tracked internally but not shown on invoices. **Best for**: Fixed-price engagements or clients who prefer simplicity. ### Setup 1. Estimate average LLM cost per agent hour. 2. Add it to the agent's billable rate. 3. Exclude LLM expenses from invoices (mark as non-billable). ### Example | Line Item | Amount | |---|---| | Agent work: 6h @ £80/hr (includes LLM) | £480 | | **Total** | **£480** | ## Pattern 3: Cost-Plus LLM costs are invoiced at cost plus a margin (e.g., cost × 1.2). **Best for**: Agencies managing multiple models with varying costs. ### Setup 1. Track LLM costs as expenses. 2. Apply a markup in the invoice (manually adjust the expense amount). ### Example | Line Item | Amount | |---|---| | Agent work: 6h @ £75/hr | £450 | | LLM tokens: 180k @ £0.003/k + 20% | £0.65 | | **Total** | **£450.65** | ## Choosing a Pattern | Factor | Pass-Through | Bundled | Cost-Plus | |---|---|---|---| | Client transparency | High | Low | Medium | | Billing simplicity | Medium | High | Medium | | Margin predictability | High | Medium | High | | Best for | Hourly clients | Fixed-price | Agencies | --- ## Example: Claude Code Wrapper source: https://keito.ai/docs/examples/claude-code # Example: Claude Code Wrapper > Copy-paste this example to get started. Modify the project and task IDs to match your workspace. ## What This Does A shell script that wraps a Claude Code session with Keito time tracking. It starts a timer before launching Claude Code and logs the time and token cost when the session ends. ## Prerequisites - Keito CLI installed and authenticated - Claude Code installed - `KEITO_API_KEY` and `KEITO_ACCOUNT_ID` environment variables set ## Full Code ```bash #!/bin/bash # keito-claude.sh — Track Claude Code sessions with Keito set -e PROJECT="${1:?Usage: keito-claude.sh [task-slug]}" TASK="${2:-development}" SESSION_ID=$(uuidgen) echo "Starting Keito tracking for project: $PROJECT" # Start the timer keito time start \ --project "$PROJECT" \ --task "$TASK" \ --source agent \ --agent-id "claude-code" \ --session-id "$SESSION_ID" # Run Claude Code (interactive session) claude "$@" || true # Stop the timer keito time stop --notes "Claude Code session" echo "Session tracked. ID: $SESSION_ID" ``` ## How It Works 1. Takes a project slug as the first argument and an optional task slug. 2. Generates a unique session ID for correlation. 3. Starts a Keito timer with `source: agent` and agent metadata. 4. Launches Claude Code — the user works interactively. 5. When Claude Code exits, stops the timer and logs the session. ## Customisation - **Add token logging**: If your Claude Code wrapper outputs token usage, parse it and add `keito expense log` after stopping the timer. - **Auto-detect project**: Read from a `.keito.toml` file in the repo root instead of requiring a CLI argument. - **Git integration**: Include the current branch name or commit range in the notes. ## Usage ```bash # Basic usage ./keito-claude.sh acme-website # With a specific task ./keito-claude.sh acme-website code-review # Make it executable chmod +x keito-claude.sh ``` --- ## Example: GitHub Actions source: https://keito.ai/docs/examples/github-actions # Example: GitHub Actions > Copy-paste this workflow into `.github/workflows/ai-review.yml`. Update the project slug and review script path. ## What This Does A GitHub Actions workflow that runs an AI code review on every pull request, tracking the time and token cost in Keito. ## Prerequisites - `KEITO_API_KEY` stored as a GitHub secret - `KEITO_ACCOUNT_ID` stored as a GitHub secret - `PROJECT_SLUG` set as a GitHub variable - An AI review script at `./scripts/ai-review.sh` ## Full Code ```yaml name: AI Code Review on: [pull_request] jobs: review: runs-on: ubuntu-latest env: KEITO_API_KEY: ${{ secrets.KEITO_API_KEY }} KEITO_ACCOUNT_ID: ${{ secrets.KEITO_ACCOUNT_ID }} steps: - uses: actions/checkout@v4 - name: Install Keito CLI run: npm install -g @keito/cli - name: Start tracking run: | keito time start \ --project ${{ vars.PROJECT_SLUG }} \ --task code-review \ --source agent \ --agent-id "gh-actions-reviewer" \ --session-id "${{ github.run_id }}" - name: Run AI code review id: review run: | ./scripts/ai-review.sh > review-output.txt 2>&1 echo "token_count=$(grep -oP 'tokens: \K[0-9]+' review-output.txt || echo 0)" >> $GITHUB_OUTPUT - name: Stop tracking and log cost if: always() run: | keito time stop \ --notes "AI code review for PR #${{ github.event.pull_request.number }}: ${{ github.event.pull_request.title }}" if [ "${{ steps.review.outputs.token_count }}" -gt 0 ]; then keito expense log \ --project ${{ vars.PROJECT_SLUG }} \ --quantity ${{ steps.review.outputs.token_count }} \ --notes "Code review tokens for PR #${{ github.event.pull_request.number }}" fi ``` ## How It Works 1. Triggers on every pull request. 2. Installs the Keito CLI from npm. 3. Starts a timer with the GitHub run ID as the session ID. 4. Runs your AI review script and captures the token count from output. 5. Stops the timer and logs an LLM expense (if tokens were used). 6. The `if: always()` ensures tracking stops even if the review step fails. ## Customisation - **Multiple reviewers**: Run different AI tools in parallel steps, each with their own `agent-id`. - **Comment on PR**: Add a step to post the review output as a PR comment using `gh pr comment`. - **Branch filtering**: Add `branches: [main]` to only review PRs targeting main. --- ## Example: LangChain Agent source: https://keito.ai/docs/examples/langchain-agent # Example: LangChain Agent > Copy-paste this example into a Python file. Install dependencies and set environment variables. ## What This Does A LangChain agent that researches a topic and generates a summary, with automatic time and token tracking via Keito. ## Prerequisites - Python 3.10+ - `pip install keito-python langchain-anthropic langchain-core` - `KEITO_API_KEY`, `KEITO_ACCOUNT_ID`, and `ANTHROPIC_API_KEY` environment variables set ## Full Code ```python import os from keito import Keito from keito.integrations.langchain import KeitoCallbackHandler from langchain_anthropic import ChatAnthropic from langchain_core.prompts import ChatPromptTemplate from langchain_core.output_parsers import StrOutputParser # Initialise Keito keito = Keito() # Create the callback handler handler = KeitoCallbackHandler( client=keito, project_id="prj_abc", task_id="tsk_research", agent_id="research-agent-01", ) # Build the chain llm = ChatAnthropic(model="claude-sonnet-4-6", temperature=0) prompt = ChatPromptTemplate.from_messages([ ("system", "You are a research analyst. Provide concise, factual summaries."), ("user", "Research and summarise: {topic}"), ]) chain = prompt | llm | StrOutputParser() # Run with automatic tracking result = chain.invoke( {"topic": "AI agent billing best practices for consultancies"}, config={"callbacks": [handler]}, ) print(result) ``` ## How It Works 1. Initialises the Keito client (reads API key from environment). 2. Creates a `KeitoCallbackHandler` with project, task, and agent details. 3. Builds a standard LangChain chain (prompt → LLM → output parser). 4. Invokes the chain with the Keito handler as a callback. 5. The handler automatically creates a time entry and LLM expense when the chain completes. ## Customisation - **Change the model**: Replace `claude-sonnet-4-6` with any supported model. - **Add tools**: Convert to an agent with tools — the handler tracks all LLM calls within the agent run. - **Batch processing**: Wrap in a loop to process multiple topics, each tracked separately. ## Output in Keito After running, you'll see in the Keito web app: - A time entry with a violet **Agent** badge, attributed to "research-agent-01" - An LLM expense showing the token count and cost - Both linked by a shared `session_id` in their metadata --- ## Changelog source: https://keito.ai/docs/changelog/changelog # Changelog ## March 2026 ### Agent Tracking & Developer Platform Keito now treats AI agents as first-class team members. This release adds comprehensive support for tracking agent work alongside human work. **New Features** - **Agent user type** — create agent profiles with their own rates, project assignments, and API keys. - **Source field** — every time entry and expense now has a `source` field (`web`, `cli`, `api`, `agent`) for filtering and reporting. - **Metadata field** — attach structured JSON context to entries, including `agent_id`, `session_id`, `model`, and token counts. - **Visual badges** — violet "Agent" badge and amber "CLI" badge on entries in day view, week view, and expenses list. - **LLM Usage expense category** — built-in category for tracking AI model token costs, seeded in every workspace. - **Source filtering** — filter time entries, expenses, and reports by source to analyse agent work separately. **Developer Platform** - **REST API** — full CRUD on time entries and expenses, with source filtering on list endpoints. - **Node SDK** (`@keito/sdk`) — typed TypeScript/JavaScript client with agent session helpers. - **Python SDK** (`keito-python`) — Python client with context manager, decorator, and async patterns. - **CLI** — command-line tool with auto-detection for Claude Code, Codex, and other agent environments. - **LangChain integration** — callback handler for automatic time and token tracking. - **CrewAI integration** — task callback for multi-agent crew tracking. **Documentation** - New Developer Docs section with API reference, SDK guides, and integration patterns. - Updated Platform Guides with agent-aware content for time tracking, expenses, invoicing, reports, and team management. --- ## Company Settings source: https://keito.ai/docs/settings/company-settings # Company Settings Configure your workspace to match how your organisation operates. ## Company Profile ### Basic Information - **Company name** — Displayed throughout the app and on invoices. - **Address** — Your company's mailing address. Appears on invoices. - **Phone** — Company phone number. - **Subdomain** — Your custom Keito URL (e.g., yourcompany.keito.app). ### Branding - **Logo** — Upload your company logo. Displayed on invoices and the workspace header. - **Accent colour** — Customise the colour scheme used on invoices and client-facing pages. ## Time & Date Preferences | Setting | Options | |---------|---------| | Timezone | All standard timezones | | Week starts on | Sunday, Monday, Tuesday, Wednesday, Thursday, Friday, Saturday | | Time format | 12-hour (2:30 PM) or 24-hour (14:30) | | Date format | Multiple regional formats | | Time display | Decimal (2.50) or Hours:Minutes (2:30) | ## Number Formatting - **Decimal separator** — Period (.) or comma (,) - **Thousands separator** — Comma (,), period (.), or space ## Time Tracking Settings ### Entry Defaults - **Default mode** — Timer or Manual entry as the default for new entries. - **Require notes** — When enabled, team members must add a note to every time entry. ### Entry Locking - **Lock entries after X days** — Prevent editing of entries older than a specified number of days. - Set to 0 to disable locking. ### Rounding Automatically round time entries when saved: | Option | Rounds to nearest | |--------|-------------------| | None | No rounding (exact duration) | | 5 minutes | 0:05 increments | | 6 minutes | 0:06 increments (1/10 hour) | | 10 minutes | 0:10 increments | | 15 minutes | 0:15 increments (1/4 hour) | | 30 minutes | 0:30 increments (1/2 hour) | ### Weekly Capacity Set the default expected work hours per week for your team. Individual members can have this overridden in their profile. ## Feature Toggles Enable or disable workspace modules: - **Expenses** — Turn off if your team doesn't track expenses. - **Invoicing** — Turn off if you use external invoicing software. - **Approvals** — Turn off if you don't need timesheet approval workflows. Disabled features are hidden from the navigation and are not accessible to any team member. ## Notification Settings ### Timesheet Reminders - **Enabled** — Send weekly reminders to submit timesheets. - **Reminder day/time** — When to send the initial reminder (e.g., Friday 9 AM). - **Deadline day/time** — When to send the final deadline reminder (e.g., Friday 5 PM). Reminders are sent to team members who have unsubmitted entries for the current period. ## Who Can Change Settings Only **Administrators** can modify company settings. Changes take effect immediately for all workspace members. --- ## Personal Settings source: https://keito.ai/docs/settings/personal-settings # Personal Settings Customise your individual Keito experience without affecting other team members. ## Profile Information Update your personal details: - **First name** and **Last name** — How you appear across the workspace. - **Email address** — Used for login and notifications. - **Phone number** — Optional contact number. - **Timezone** — Your local timezone. Affects how times are displayed and when you receive reminders. ## Notification Preferences Control which email notifications you receive: ### Activity Notifications - **Timesheet approved** — When your submitted timesheet is approved. - **Timesheet rejected** — When your submission is sent back for corrections. - **Project assignment** — When you're added to a new project. - **Over-budget alert** — When a project you manage crosses its budget threshold. ### Reminders - **Daily personal reminder** — A daily nudge to log your time. - **Weekly timesheet reminder** — Reminder to submit your timesheet before the deadline. - **Weekly time report** — A summary of your tracked hours for the week. ### Marketing - **Product updates** — Occasional emails about new Keito features and improvements. Each notification type can be independently enabled or disabled. ## Display Preferences Some display options follow your personal settings rather than the company default: - **Timezone** — Override the company timezone for your own view. - **Time format** — 12-hour or 24-hour preference. ## Changing Your Password Password management is handled through WorkOS AuthKit: 1. Go to your profile settings. 2. Click **Change Password** or use the SSO provider's account settings. 3. Follow the authentication provider's password change flow. ## Account Security - Keito uses WorkOS for authentication, supporting SSO and OAuth 2.0. - If your organisation uses SAML SSO, authentication is managed through your identity provider. - Session management is handled automatically — you'll be signed out after extended inactivity. --- ## Plans & Pricing source: https://keito.ai/docs/billing/plans-pricing # Plans & Pricing Keito offers flexible plans to match your team's needs as you grow. ## Available Plans ### Free Get started with core time tracking at no cost. - Basic time tracking - Limited projects and team members - Standard reports ### Pro For growing teams that need full project management and invoicing capabilities. - Everything in Free - Unlimited projects - Expense tracking - Invoicing - Budget tracking - Advanced reports - Data export (CSV and Excel) - Approval workflows - Email notifications and reminders ### Business For organizations that need integrations and advanced features. - Everything in Pro - Xero accounting integration - Priority support - Advanced team management - Custom branding on invoices ## Subscription Management ### Viewing Your Plan Go to **Settings** or the **Subscribe** page to see your current plan, billing period, and next payment date. ### Upgrading Upgrade at any time from the subscription page: 1. Click **Upgrade**. 2. Select your new plan. 3. Complete payment through Stripe. 4. New features are available immediately. ### Downgrading Downgrade to a lower plan at the end of your current billing period. Features exclusive to your current plan will remain available until the period ends. ### Cancellation Cancel your subscription from the billing page: - Your workspace remains accessible until the end of the current billing period. - After the period ends, access is restricted to read-only. - Your data is preserved and accessible if you resubscribe. ## Billing Details - **Payment method** — Credit card via Stripe. - **Billing cycle** — Monthly. - **Invoices** — Available from Stripe for your records. ## Free Trial New workspaces can start with a free trial of Pro or Business features: - Full access to all features during the trial period. - No credit card required to start. - At the end of the trial, choose a paid plan or continue with Free. ## Beta Program Invited beta users receive extended trial access: - Enter your beta invite code during onboarding. - Receive a trial period (typically 14 days) with full feature access. - At the end of the beta trial, subscribe to continue using premium features. ## Seats and Pricing Pricing is based on the number of active team members in your workspace: - Only **Active** members count toward your seat limit. - **Pending** (invited but not joined) and **Archived** (deactivated) members don't count. - Add or remove seats as your team changes — billing adjusts automatically. ## Payment Security All payment processing is handled by Stripe. Keito never stores credit card numbers or sensitive financial data directly.