JavaScript / TypeScript SDK
Preview. The source lives at
packages/sdk-jsin the public repo. The npm package is not published yet; install from the repo or copy the snippet you need. All examples on this page work today againsthttps://humanhours.dev/api/v1/*.
# Once the package ships:
npm install @humanhours/sdk
# In the meantime:
git clone https://github.com/triadgit/agent-metrics
cd agent-metrics/packages/sdk-js && pnpm install && pnpm build
60-second quickstart
import { Humanhours } from "@humanhours/sdk";
const hh = new Humanhours({ apiKey: process.env.HUMANHOURS_API_KEY! });
await hh.track({
agent_id: "support-classifier",
task_type: "email_classification",
outcome: "success",
});
Time + track in one call
const result = await hh.withTask(
{ agent_id: "support-classifier", task_type: "email_classification" },
() => classifyEmail(subject),
);
withTask measures wall-clock time for the wrapped fn, captures the outcome, and tracks it. Throws inside fn are reported as outcome="failure" and re-raised so your control flow is unchanged.
Reading numbers back
await hh.summary({ period: "30d" });
await hh.agents();
await hh.reports.raw("/time-saved?period=30d&group_by=agent");
Idempotency
Every track() call sends a fresh UUID Idempotency-Key. Pass your own when you have a stable identifier (ticket id, message id):
await hh.track({...}, { idempotencyKey: ticketId });
The server returns the original event_id on a replay within 24h, so retries are safe.
Errors
HumanhoursError carries the structured error code from the API.
import { HumanhoursError } from "@humanhours/sdk";
try {
await hh.track({...});
} catch (e) {
if (e instanceof HumanhoursError && e.code === "unknown_task_type") {
// pass human_baseline_minutes inline, or POST /v1/task-types to register one
}
throw e;
}
Runtime support
Native fetch, zero dependencies. Works on Node 18+, Deno, Bun, Cloudflare Workers, Vercel Edge, modern browsers.