Wire contract
The server keeps these payloads backwards-compatible; their shape is part of the Costpilot API contract.
POST /v1/session-map
Sent by the hook on every prompt, and again when the turn ends:
{
"session_id": "<session id of the tool>",
"issue_id": "PROJ-123",
"branch": "feature/PROJ-123-login",
"repo": "owner/name",
"agent": "claude-code",
"project_key": "PLAT",
"reported_at": "2026-10-23T09:12:44.120Z"
}
session_idandissue_idare required; everything else is optional. The server answers200 {}or400 {}.agentisclaude-code,gemini,codexorcursor.session_idis whatever id the tool also puts on its telemetry: Claude Code and Gemini CLIsession.id, Codexconversation.id, Cursorconversation_id.eventis absent (orprompt) on a prompt report andstopon a turn-end report; unknown values are accepted and create no turn. Each prompt report is stored as one turn of its session; reports for the same session within 5 seconds (byreported_at) count once. Astopreport marks the end of a turn and creates none: when it names another branch than the turn started on, the turn and everything recorded for it move to that branch. Agents sendstoponly to servers whose agent config saysturnEndReports: true.reported_atis the reporting machine's clock (ISO). It becomes the turn's time, so prompts and model calls line up even when machine and server disagree about the time. Without it the server uses its own clock.- Without a recognisable issue id the hook sends the branch name (cut to 20
characters plus
…when longer) asissue_id. The server keeps it as thebranchdimension and counts the usage as "No issue". - Issue id pattern: the project's
issueKeyPatternfrom the central config when the session belongs to a project that has one, otherwise the server'sdefaultIssueKeyPattern, otherwise the built-in[A-Z][A-Z0-9]+-\d+(?:\.\d+)*. repois sent with every mapping, also on a branch without an issue key: the server derives the project from the repository when the issue key matches no project.project_keyis sent when the session belongs to a project by repository, and always when--project-keyis set (an unknown key is reported as is). It lands in the metadata of the session's calls.- When the server sets
COSTPILOT_INGEST_TOKEN, the request carriesAuthorization: Bearer <secret>. - The request times out after 1.5 seconds. Errors are swallowed and the hook always exits 0. A hook must never block the tool.
GET /v1/agent-config
Read by the hook before each report (cached for 5 minutes, 1 second request
timeout; on errors the cached answer is used, a 404 from an older server is
cached as "no config"). See
Central configuration for the
payload. GET /v1/projects/:projectKey returns one project in the same
shape, 404 for an unknown key.
Telemetry
The tools themselves send OTLP JSON to POST /v1/logs and
POST /v1/metrics. The attributes Costpilot reads per emitter are
described in the telemetry data model. A
pipeline that runs without the hook passes attribution through
OTEL_RESOURCE_ATTRIBUTES instead, see
Agents in CI pipelines.