Skip to main content

What posthook data looks like

Posthook runs on each engineer’s machine and records what Claude Code, Cursor and Codex CLI wrote, what the engineer prompted, and which of those lines were committed. Bilanc models it into four tables, all reachable through the metrics endpoint: Two more posthook metrics live on the pull_requests table because they measure AI lines that reached merged PRs: posthook-merged-ai-lines and posthook-ai-pct-of-code. All four posthook tables carry the session owner’s Bilanc identity (merged_user_id, team_ids, squad and manager details), so every people filter and the same row-level security apply as elsewhere.
Only sessions from engineers whose posthook identity is mapped to a Bilanc user are returned. Sessions from unmapped emails are excluded, matching the dashboard.

Metrics

Session summary metrics (posthook_sessions)

count
Distinct sessions that generated or committed at least one AI line. Response column posthook_sessions_count. Matches the dashboard’s Active Sessions card. Sessions with no captured AI activity exist in the table but are not counted.
count
Distinct engineers with at least one session in the window. Response column posthook_active_users.
aggregation required
AI working time in seconds, derived from hook events: each session is active for every 5-minute window containing at least one event. Response column posthook_active_time. Pass "aggregation": "SUM"; the value is always a sum regardless.
aggregation required
Input + output tokens. Response column {agg}_total_tokens. Cache-read and cache-creation tokens are excluded, matching the posthook dashboard. The raw per-session total_tokens column includes them.
aggregation required
Output tokens only. Response column {agg}_output_tokens.
aggregation required
Lines the agent wrote, from edit events. Response column {agg}_generated_lines.
aggregation required
Lines the agent wrote that posthook attributed into commits. Response column {agg}_committed_lines.
ratio
Committed ÷ generated lines. Response column posthook_ai_code_ratio.
string
Most-used model among sessions that committed AI code, ignoring placeholder slugs like default and unknown. Response column posthook_top_model.

Detail-table counts

count
Distinct messages in posthook_session_messages. Response column posthook_session_message_counts.
count
Rows in posthook_session_files. Response column posthook_session_file_counts.
count
Distinct commits in posthook_session_commits. Response column posthook_session_commit_counts.

PR-level attribution (pull_requests)

aggregation required
AI-attributed lines that landed in merged PRs. Response column {agg}_merged_ai_lines. Uses PR date fields (pr_merged_at, pr_created_at, …) and PR filters.
ratio
AI committed lines ÷ lines added across the selected PRs, capped at 1. Response column posthook_ai_pct_of_code. The dashboard’s “AI % of Code” card requests this with "pr_states": ["merged"] and "date_field": "pr_merged_at".

Columns

Any column can be used in group_by and order_by; date-typed columns can be used in date_field.

posthook_sessions

Example: team summary by agent, week over week

This is the request behind the Posthook team table in the dashboard. Note posthook-ai-pct-of-code is a PR-table metric, so it gets pr_merged_at as its date field and the pr_states filter; the session-table metrics ignore pr_states.
Cursor reports no token counts through its hooks, so token metrics for agent_slug = cursor are zero by design.

Example: every session as a row

Group by session_id and the columns you want. This is the request behind the dashboard’s session list. metric_min_value: 1 keeps only sessions that generated or committed AI code; drop it to include every captured session.
For more than 200 sessions, window by day or use get-multiple-metrics. See Paging through large tables.

Example: a session’s prompts and replies

Pin the query to one session with filters.session_id. Set start_date a day or two before the session started: the date column of a message falls back to the session date, but commits attributed later can land after the session’s own day.
To export prompts in bulk rather than per session, drop session_id and window by date instead, adding session_id and engineer_email to group_by so each message keeps its context.

Example: files touched and commits attributed

The same shape, against the other two detail tables. These are the dashboard’s “Files” and “Commits” panels.

Example: daily usage per engineer

A conventional aggregate: tokens, active time and committed lines per engineer per day.

Parameter notes

string
All four posthook tables default to date. Sessions also accept started_at and ended_at; messages message_ts; files first_edit_ts and last_edit_ts; commits committed_at. The two PR-table metrics take PR date fields (pr_merged_at, pr_created_at, pr_closed_at, …).
object
Posthook tables honour member, team, departments, locations, levels, squad_ids, squad_levels, manager_ids and session_id. They have no repository or PR-state columns, so repositories and pr_states are ignored for them (filter on the repo_name column via group_by instead). posthook-merged-ai-lines and posthook-ai-pct-of-code are PR-table metrics and honour every PR filter.
string
Required for posthook-total-tokens, posthook-output-tokens, posthook-generated-lines, posthook-committed-lines, posthook-active-time and posthook-merged-ai-lines. Use SUM for totals and AVG for per-session averages. Count, ratio and top-model metrics take none.