Agents and the API
Your coding agents keep the record. You decide what counts.
Two ways, and you can use both. Install one hook and the time a project consumes — yours and your agents' — is recorded as it happens. Or just ask an agent to start a timer or log the hour. Both land as drafts you review.
Shyre is in early access and accounts are set up by hand. Tell us what you work on and we'll be in touch.
Setup is one evening, once
Each developer does this once, on their own machine. Nothing is deployed and nothing runs on a server.
Sign in from the agent, once
Add Shyre's MCP server and the agent asks you to sign in: a browser tab, the team it acts on, one Allow. It holds a token nobody typed. The plugin's hooks still read a key from your shell profile, and a key still works for a machine with no browser.
A plugin, or one installer
Claude Code and the Codex CLI install it as a plugin from the same marketplace; Cursor gets the same runtime from one installer command. Either way the hooks fire for every session in every repository. (Codex inside the VS Code extension cannot approve hooks, so sessions there are not recorded; the MCP server still works there.)
Sessions emit heartbeats
Tool calls, prompts and completed subagents leave precise activity marks as the session runs.
Hours appear
Shyre groups the activity into attributed, categorized time, ready to review before any of it counts.
Or just ask it
The hook records without being asked. The second way is that you can say it — in the same conversation you are already having.
- You say it in words
- “Start a timer on this.” “Log the last hour to the migration.” “What am I tracking right now?” Plain sentences, no syntax to learn.
- It works out which project
- It reads the repository you are in and matches it against your live project list, so “this” means the project you are actually sitting in.
- It writes the description
- It is holding the branch, the ticket and the diff, so the line on your timesheet says what you did — not “work”.
- It cannot touch your timer
- An agent starting a timer while yours is running is refused, not silently swapped. It cannot stop yours either — the database rejects a stop on any timer an agent did not start. It keeps its own record beside yours; it never edits yours.
It infers, so it can infer wrong. Every entry it writes is a draft you can correct or strike — the same review the automatic ones get.
What actually gets measured
Most time-tracking marketing is vague about this on purpose. Here is the whole mechanism, because it is the first thing anyone will ask.
- Activity, sampled
- Every tool call, prompt and subagent finish appends a heartbeat on the developer's machine, and the entry is the sum of the gaps between them. The marks come from what the agent did — no keystrokes, no screenshots, no idle-mouse detection, nothing that watches a person.
- Idle time, dropped
- Any gap longer than the idle cap is thrown away. Lunch, a meeting, a laptop left open overnight — none of it becomes an hour.
- Machine hours, counted and labeled
- A long unattended run is dense with tool-call heartbeats, so it becomes time — labeled as machine hours, never quietly folded into somebody's. Whether those hours belong on a client's invoice, or in what a project cost your team, is a decision this makes visible rather than one it makes for you.
- Attribution, immutable
- Which agent, which session, on whose behalf. The database refuses to let it be edited afterwards, including by an administrator.
- The backstop stands down
- An agent can log its own work through the API, and the hook fills in the rest of the session that agent just ran, by extending the agent's own entries. The hook checks first, by the minute, across every project, and stands down for any stretch already covered.
- Overlap is refused, with the fix attached
- Two entries cannot claim the same minute on the same project — a conflicting write is rejected with the fix attached. Across projects, when two of your agent sessions claim the same minute, it is split where your prompts went, or evenly, and the day says so on one line with an Undo that puts both entries back exactly. Any other pair is recorded and left for you to resolve.
This is the whole payload
“No source code leaves the machine” is easy to write. Here is the request.
The recorder is one Node file you can read before you run, shipped inside the Claude Code plugin and installed for other agents by its own installer. When a session ends it does not post an entry of its own: each stretch of activity no entry covers extends the agent entry beside it on the same project, and this request is all it sends — the stretch's start and end, a key that stops the same stretch being counted twice, the agent's label, the session id, two meters — how long the machine was working, and how long it sat waiting on you — and the instants you sent a prompt, so an overlap between two sessions can be split where your attention went. Three more ride only when the agent's own metrics exporter is on: the tokens the session spent, by type, for the model that spent the most; that model's name; and the client's own list-price estimate of the whole session. A session that reports none sends none — never a zero. The meters add to the entry's, and tokens only for the same model. Timestamps, not prompts. No file names. No prompt text. No diff. Nothing that says what the code does.
POST /api/v1/entries/<id>/fold
{
"start_time": "…",
"end_time": "…",
"idempotency_key": "…",
"agent_label": "Claude Code",
"agent_runtime_min": 35,
"agent_wait_min": 12,
"prompt_marks": ["…", "…"],
"agent_tokens": {"input": 1200,
"output": 340,
"cache_read": 90000,
"cache_creation": 5000},
"agent_model": "claude-opus-5",
"agent_cost_usd_list": 0.12
}Between beats it keeps two local files per session under ~/.shyre — a small header with the repository and working directory, and a list of timestamps, each tagged with which hook wrote it. At session end those become a spool file per run of activity, kept until delivered and for at most thirty days. A stretch with no agent entry beside it on its project is held for a day in case a parallel session logs one, then posted as an entry of its own, so time nobody logged is never lost; only a stretch under two minutes is dropped, and that is reported to the server so it shows in your activity list instead of vanishing. It also appends a line to a local log (~/.shyre/refusals.log, or $SHYRE_HOOK_LOG) whenever a run's time is folded, refused, already covered, or belongs to a repository no project names, so time can never go missing silently; that file records the project and the working window, and it persists until you delete it. Each request also names the recorder's version, the agent, and a random id that tells one install from another (made on your machine, and one setting sends none); Shyre keeps those three, with which key that recorder last called with and when it was first and last heard from — no hostname, no path, no IP address — so the Integrations page can say when a recorder is out of date or has gone quiet — and so Shyre can tell you itself, after three working days of silence, before an invoice comes up short.
Installed mid-week? The plugin's backfill command reconstructs the sessions before that from the transcripts Claude Code already keeps on your machine — opt-in, run by hand, per machine — taking only the timestamp, the type, whether the line is a tool result, the working directory and the session id of each line, under the same idle rule and the same no-double-counting check. Those days usually have no entries to extend, so a backfilled run is posted as an entry of its own, marked backfilled, and every entry row badges it, so reconstructed time never passes for live time.
Three meters, never added to your hours
Three meters ride beside every entry. The two time meters partition the session: every moment inside it is either the machine working or the agent waiting on you — nothing is counted twice and nothing is left out. (They are each rounded down to the minute, so the two printed figures can land a minute under the recorded time.) Neither is ever summed into your hours, multiplied by a rate, or placed on an invoice — they sit beside the entry, not inside it. The waiting figure is not wasted time; you are usually reading the diff. It is there because it is the one number you can act on: it tells you when you were the bottleneck. Both meters come out in the CSV export, as their own columns, and Reports reads them back per project and per customer: agent runtime against the hours people declared, and how long the agent waited on a review. The third meter is tokens: recorded per model as the agent reports them, valued at list price as an estimate — on a subscription the payable is zero — never summed across models or vendors, and, like the other two, never on an invoice.
What the key can reach, and what it cannot
You are about to hand a coding agent write access to the record you bill from. These are the walls.
Scopes a key can carry
No scope exists for these
Not “off by default” — there is no scope to grant. A key cannot be issued with reach it does not have.
It cannot touch time a person logged
Edit and delete are refused on any entry whose recorded origin is not an agent.
It cannot see a rate
Not the customer's, not a colleague's, not the cost of an hour.
It cannot retract what a client has seen
Agent-posted release notes arrive as drafts. Publishing one takes an owner's or admin's key, and the agent is told to ask you first; unpublishing stays a human act.
It cannot take your timer
Refused while any timer runs; stopping a person's takes an explicit override.
It cannot read across the team
A key reads only its owner's entries, whatever the team's visibility setting, and rates are masked by role. A key inherits the limits of the person it belongs to; it never exceeds them.
The API is not an upgrade.
A REST surface and an MCP server, on every account, at the one price.
Sold as a tier elsewhere; here it is on every account. One implementation behind two transports, so a coding agent connects to it directly and a shell script gets the same endpoints. It covers the time record, the release notes on top of it, and the proposal in front of it: read your project context, start and stop a timer, log a finished block, correct one, draft a release, draft a proposal. Authentication is a personal access token in an Authorization header — never a query string, never a cookie, never a body field.
It reaches the time record, not the whole account
Today the surface is time, releases and proposals — the parts an agent needs to do its own bookkeeping and to draft the document the work starts from. Everything it writes lands as a draft; sending stays yours. Invoicing and customer records are not on it yet. You can still export them; you cannot yet script them.
Wire it up in an evening. Turn it off in a click.
Integrations stay off until an owner switches them on, and switching them back off refuses every existing key on its next call. Nothing about this is a one-way door.