Skip to content

osodevops/keito-node

Repository files navigation

Keito Node.js SDK

npm version CI TypeScript License: MIT Node

The official Node.js SDK for the Keito API — track billable time, expenses, and invoices for humans and AI agents.

Keito is the first purpose-built billable work tracker for AI agents, capturing not just what agents cost, but what they earn. The SDK supports time-based, outcome-based, and hybrid billing models.

Installation

npm install @keito-ai/sdk

Requirements: Node.js 18+ (uses native fetch). Zero runtime dependencies.

Quickstart

import { Keito } from '@keito-ai/sdk';

const client = new Keito({
  apiKey: process.env.KEITO_API_KEY!,
  accountId: process.env.KEITO_ACCOUNT_ID!,
});

// Log billable time for an AI agent
const entry = await client.timeEntries.create({
  project_id: 'proj_123',
  task_id: 'task_456',
  spent_date: '2026-03-05',
  hours: 1.5,
  notes: 'Automated code review for PR #842',
  source: 'agent',
  billable: true,
  metadata: {
    agent_id: 'code-reviewer-v2',
    run_id: 'run_abc123',
    model: 'claude-4-sonnet',
    tokens_used: 45200,
  },
});

console.log(entry.id);

Authentication

The SDK uses API key authentication. Pass your key and account ID when creating the client:

const client = new Keito({
  apiKey: 'kto_...',       // Your Keito API key
  accountId: 'acc_...',    // Your Keito account ID
});

We recommend using environment variables to keep your keys secure. Never commit API keys to source control.

Configuration

Option Default Description
apiKey required Your Keito API key (kto_...)
accountId required Your Keito account ID
baseUrl https://app.keito.io API base URL
maxRetries 3 Max retries for failed requests
timeout 30000 Request timeout in milliseconds
const client = new Keito({
  apiKey: 'kto_...',
  accountId: 'acc_...',
  baseUrl: 'https://app.keito.io',
  maxRetries: 3,
  timeout: 30_000,
});

Resources

The SDK follows a resource-based architecture. Each API resource is accessed as a property on the client:

Resource Methods Description
client.timeEntries list create update delete Log and manage billable time
client.expenses list create Track expenses and compute costs
client.projects list Browse projects
client.clients list get create update Manage client records
client.contacts list create Manage client contacts
client.tasks list Browse tasks
client.users me Get current user
client.invoices list get create update delete Create and manage invoices
client.invoices.messages list send Send invoices to clients
client.reports teamTime agentSummary Reporting and analytics
client.outcomes log Log outcome-based billable events

Usage Examples

Time Entries

// List time entries with filters
const entries = await client.timeEntries.list({
  project_id: 'proj_123',
  source: 'agent',
  from: '2026-03-01',
  to: '2026-03-31',
});

console.log(entries.data); // TimeEntry[]

// Create a time entry
const entry = await client.timeEntries.create({
  project_id: 'proj_123',
  task_id: 'task_456',
  spent_date: '2026-03-05',
  hours: 2.0,
  source: 'agent',
  billable: true,
});

// Update a time entry
await client.timeEntries.update('te_abc', {
  hours: 2.5,
  notes: 'Updated estimate',
});

// Delete a time entry
await client.timeEntries.delete('te_abc');

Expenses

const expense = await client.expenses.create({
  project_id: 'proj_123',
  expense_category_id: 'cat_compute',
  spent_date: '2026-03-05',
  total_cost: 4.20,
  notes: 'GPU compute for batch inference',
  source: 'agent',
  billable: true,
  metadata: {
    provider: 'aws',
    instance_type: 'p4d.24xlarge',
    duration_minutes: 45,
  },
});

Invoices

// Create an invoice
const invoice = await client.invoices.create({
  client_id: 'cl_abc',
  payment_term: 'net_30',
  subject: 'March 2026 — Agent Services',
  line_items: [
    {
      kind: 'Service',
      description: 'AI code review — 42 PRs',
      quantity: 42,
      unit_price: 15.00,
      taxed: false,
      taxed2: false,
    },
  ],
});

// Send the invoice
await client.invoices.messages.send(invoice.id!, {
  recipients: [{ name: 'Jane Smith', email: 'jane@acme.com' }],
  subject: 'Invoice from Keito',
  body: 'Please find your invoice attached.',
  attach_pdf: true,
  send_me_a_copy: false,
  include_attachments: false,
  event_type: 'send',
});

Current User

const me = await client.users.me();
console.log(me.email, me.user_type); // "agent@company.com" "agent"

Pagination

List methods return a PaginatedResponse<T> with built-in auto-pagination. You can iterate through all items across pages without managing page tokens:

// Auto-paginate through all time entries
for await (const entry of client.timeEntries.list({ source: 'agent' })) {
  console.log(entry.id, entry.hours);
}

You can also work with individual pages:

const page = await client.timeEntries.list({ per_page: 50 });

console.log(page.data);          // TimeEntry[] — current page
console.log(page.total_entries);  // total across all pages
console.log(page.total_pages);

if (page.hasNextPage()) {
  const next = await page.nextPage();
}

Error Handling

The SDK throws typed errors for different failure modes. All errors extend KeitoError:

import { Keito, KeitoApiError, KeitoRateLimitError } from '@keito-ai/sdk';

try {
  await client.timeEntries.create({ ... });
} catch (err) {
  if (err instanceof KeitoRateLimitError) {
    console.log(`Rate limited. Retry after ${err.retryAfter}s`);
  } else if (err instanceof KeitoApiError) {
    console.log(err.status);            // 400, 401, 403, 404, 409, etc.
    console.log(err.error);             // "bad_request"
    console.log(err.error_description); // "project_id is required"
  }
}
Error Class Status When
KeitoAuthError 401 Invalid or missing API key
KeitoForbiddenError 403 Insufficient permissions
KeitoNotFoundError 404 Resource doesn't exist
KeitoConflictError 409 Conflict (e.g. deleting approved entry)
KeitoRateLimitError 429 Too many requests
KeitoTimeoutError Request timed out
KeitoConnectionError Network failure

Retries

The SDK automatically retries on transient failures (status 408, 429, 500, 502, 503, 504) and network errors with exponential backoff and jitter. On 429 responses, the SDK respects the Retry-After header.

Retries are configurable:

const client = new Keito({
  apiKey: 'kto_...',
  accountId: 'acc_...',
  maxRetries: 5,    // default: 3
  timeout: 60_000,  // default: 30s
});

Outcome-Based Billing

Not all agent work is measured in hours. The SDK supports outcome-based billing for agents that deliver discrete results — resolved tickets, qualified leads, generated documents.

Logging Outcomes

The outcomes.log() method is a convenience layer over time entries. It creates a TimeEntry with hours: 0 and outcome data packed into metadata:

import { Keito, OutcomeTypes } from '@keito-ai/sdk';

const client = new Keito({ apiKey: 'kto_...', accountId: 'acc_...' });

await client.outcomes.log({
  project_id: 'proj_123',
  task_id: 'task_456',
  spent_date: '2026-03-05',
  outcome: {
    type: OutcomeTypes.TICKET_RESOLVED,
    description: 'Resolved billing inquiry #4821 via email',
    unit_price: 0.99,
    quantity: 1,
    success: true,
    evidence: {
      ticket_id: 'TKT-4821',
      resolution_time_seconds: 45,
      customer_confirmed: true,
    },
  },
  metadata: {
    agent_id: 'support-agent-v3',
    run_id: 'run_xyz789',
  },
});

Outcome Types

The SDK ships with pre-defined outcome type constants:

import { OutcomeTypes } from '@keito-ai/sdk';

OutcomeTypes.TICKET_RESOLVED        // 'ticket_resolved'
OutcomeTypes.LEAD_QUALIFIED          // 'lead_qualified'
OutcomeTypes.MEETING_BOOKED          // 'meeting_booked'
OutcomeTypes.DOCUMENT_GENERATED      // 'document_generated'
OutcomeTypes.CODE_REVIEW_COMPLETED   // 'code_review_completed'
OutcomeTypes.DATA_PROCESSED          // 'data_processed'
OutcomeTypes.EMAIL_SENT              // 'email_sent'
OutcomeTypes.INVOICE_CREATED         // 'invoice_created'
OutcomeTypes.WORKFLOW_COMPLETED      // 'workflow_completed'
OutcomeTypes.CUSTOM                  // 'custom'

You can also pass any string as a custom outcome type.

Blended Reporting

Get a combined time + outcome summary for an agent:

const summary = await client.reports.agentSummary({
  user_id: 'agent_user_123',
  from: '2026-03-01',
  to: '2026-03-31',
});

console.log(summary.time);      // { total_hours, billable_hours, revenue }
console.log(summary.outcomes);   // { total, successful, revenue }
console.log(summary.combined);   // { total_revenue }

Metadata

Time entries and expenses support a metadata field (arbitrary JSON, max 4KB) for storing agent-specific context. We recommend a consistent schema:

await client.timeEntries.create({
  project_id: 'proj_123',
  task_id: 'task_456',
  spent_date: '2026-03-05',
  hours: 1.5,
  source: 'agent',
  metadata: {
    // Agent identity
    agent_id: 'code-reviewer-v2',
    framework: 'openclaw',

    // Run context
    run_id: 'run_abc123',
    parent_run_id: 'run_parent456',
    trigger: 'webhook',

    // Model usage
    model_provider: 'anthropic',
    model_name: 'claude-4-sonnet',
    tokens_in: 12500,
    tokens_out: 32700,
    cost_usd: 0.18,

    // Quality
    confidence: 0.94,
    human_reviewed: false,
  },
});

Metadata uses merge semantics on update — new keys are merged into existing metadata. Set metadata to null to clear it.

TypeScript

The SDK is written in TypeScript and exports types for all API models:

import type {
  TimeEntry,
  TimeEntryCreate,
  Expense,
  Invoice,
  Project,
  Client,
  User,
  Metadata,
} from '@keito-ai/sdk';

All method parameters and return types are fully typed. Your editor will provide autocomplete and type checking out of the box.

Spec Sync

SDK types are auto-generated from the Keito OpenAPI v2 spec using openapi-typescript. When the spec changes:

npm run generate   # regenerate src/generated/openapi.d.ts
npm run typecheck  # verify all resource classes still compile

The CI pipeline includes a workflow that auto-opens a PR when the upstream spec changes.

Contributing

Contributions are welcome. To get started:

git clone https://github.com/osodevops/keito-node.git
cd keito-node
npm install
npm run generate
npm test

Development Scripts

Script Description
npm run generate Regenerate types from OpenAPI spec
npm run typecheck Run TypeScript type checking
npm test Run tests with Vitest
npm run build Build ESM + CJS output

License

MIT

About

Official Node.js SDK for the Keito API — track billable time, expenses, and invoices for humans and AI agents

Resources

Contributing

Stars

Watchers

Forks

Packages

 
 
 

Contributors