Transaction Log Format
Nia maintains a machine-readable transaction log in JSON Lines (JSONL) format at .nia/work/<job_id>/logs/transaction.jsonl. Each workflow execution is logged for enterprise reporting, cost tracking, and usage analytics.
File Location
.nia/work/
└── <job_id>/
└── logs/
└── transaction.jsonl
Example: .nia/work/job_399/logs/transaction.jsonl
Format
Each line in the transaction log is a JSON object representing a single event. Events are written as they occur during workflow execution.
Event Types
Workflow Event
Logs the execution of a workflow command (e.g., nia issue draft, nia code implement).
{
"event_type": "workflow",
"command": "issue draft",
"start_time": "2026-04-20T23:34:22.753Z",
"start_commit_sha": "dd014a85",
"end_time": "2026-04-20T23:35:45.123Z",
"end_commit_sha": "98cec87b",
"trace_file": "traces/20260420_233418_issue.trace.md",
"success": true,
"model": "claude-sonnet-4.5",
"role": "product_manager",
"custom_agent": "none",
"token_usage": {
"input_tokens": 608900,
"cached_tokens": 556700,
"output_tokens": 5100
}
}
Fields
| Field | Type | Required | Description |
|---|---|---|---|
event_type | string | Yes | Always "workflow" for workflow events |
command | string | Yes | The workflow command executed (e.g., "issue draft") |
start_time | string | Yes | ISO 8601 timestamp when workflow started |
start_commit_sha | string | No | Git commit SHA at workflow start (omitted if not in git repo) |
end_time | string | No | ISO 8601 timestamp when workflow completed |
end_commit_sha | string | No | Git commit SHA at workflow completion |
trace_file | string | No | Path to trace file (relative to job directory) |
success | boolean | No | Whether the workflow completed successfully |
error_message | string | No | Error message if workflow failed |
token_usage | object | No | Token consumption statistics (see below) |
model | string | Yes | AI model used (e.g., "claude-sonnet-4.5"). Value is "not set" for start events or when unavailable |
role | string | Yes | Role prompt used (e.g., "product_manager"). Value is "none" when custom agent is used, "not set" for start events |
custom_agent | string | Yes | Custom agent name (e.g., "python-expert"). Value is "none" when not configured, "not set" for start events |
Note on Agent Configuration Fields: The
model,role, andcustom_agentfields are always present in workflow events. When a value is not applicable, the field contains a descriptive sentinel string ("not set"or"none") rather than being omitted or set tonull. This ensures a consistent schema for reporting and analytics.
- Use
"not set"sentinel to indicate the value wasn’t available at event time (e.g., start events)- Use
"none"sentinel to indicate intentional absence (e.g., no custom agent configured)roleandcustom_agentare mutually exclusive in completion events: whencustom_agenthas a value,rolewill be"none"
Token Usage Object
The token_usage field captures AI agent token consumption for cost tracking. This field is optional and only present when the agent supports token reporting.
| Field | Type | Description |
|---|---|---|
input_tokens | integer | Number of tokens sent to the model (prompt size) |
cached_tokens | integer | Number of tokens served from cache (reduces cost) |
output_tokens | integer | Number of tokens generated by the model (completion size) |
reasoning_tokens | integer | Number of reasoning tokens used (optional, when available) |
Supported Agents:
- ✅ GitHub Copilot CLI (parses token usage from stderr or stdout via STDERR:-prefixed lines)
- ✅ Claude Code CLI (parses token usage from stream-json events)
- ✅ OpenCode (parses token usage from step_finish events)
- ❌ Gemini CLI (returns
None, field is omitted from JSON)
Token Units: All token counts are in individual tokens, not thousands.
Example Calculation:
{
"input_tokens": 608900, // 608.9k tokens input
"cached_tokens": 556700, // 556.7k tokens cached (not charged)
"output_tokens": 5100 // 5.1k tokens output
}
To calculate billable tokens:
billable_input = input_tokens - cached_tokens
billable_input = 608900 - 556700 = 52200 tokens (52.2k)
billable_output = 5100 tokens (5.1k)
Agent Configuration Fields
The model, role, and custom_agent fields capture the effective AI agent configuration used for workflow execution. These fields enable cost tracking, performance analysis, and audit trails.
Example with Standard Role:
{
"event_type": "workflow",
"command": "issue draft",
"model": "claude-sonnet-4.5",
"role": "product_manager",
"custom_agent": "none",
"success": true
}
Example with Custom Agent:
{
"event_type": "workflow",
"command": "code implement",
"model": "gpt-5.4",
"role": "none",
"custom_agent": "python-expert",
"success": true
}
Example Start Event:
{
"event_type": "workflow",
"command": "issue draft",
"start_time": "2026-04-20T23:34:22.753Z",
"model": "not set",
"role": "not set",
"custom_agent": "not set"
}
Sentinel Values:
"not set": Value wasn’t available at event time (used in start events before configuration resolution)"none": Intentional absence (e.g., no custom agent configured, or role not used because custom agent was used)
Use Cases:
- Cost Tracking: Group workflows by
modelto calculate usage costs per model tier - Performance Analysis: Compare workflow success rates and durations across different models
- Troubleshooting: Identify which model and configuration were used for problematic workflows
- Audit Trail: Track which roles and custom agents were used for compliance and review purposes
Utility Event
Logs utility command execution (e.g., nia config show, nia guide). In addition to the local JSONL log, this event is forwarded to App Insights (consent-gated), the same way workflow events are.
{
"event_type": "utility",
"command": "config show",
"timestamp": "2026-04-20T23:30:15.000Z",
"success": true
}
Example Log File
{"event_type":"workflow","command":"issue draft","start_time":"2026-04-20T23:34:22.753Z","start_commit_sha":"dd014a85","end_time":"2026-04-20T23:35:45.123Z","end_commit_sha":"98cec87b","trace_file":"traces/20260420_233418_issue.trace.md","success":true,"model":"claude-sonnet-4.5","role":"product_manager","custom_agent":"none","token_usage":{"input_tokens":608900,"cached_tokens":556700,"output_tokens":5100}}
{"event_type":"workflow","command":"code implement","start_time":"2026-04-20T23:40:10.000Z","start_commit_sha":"98cec87b","end_time":"2026-04-20T23:45:30.500Z","end_commit_sha":"a1b2c3d4","trace_file":"traces/20260420_234010_code.trace.md","success":true,"model":"gpt-5.4","role":"none","custom_agent":"python-expert","token_usage":{"input_tokens":420000,"cached_tokens":380000,"output_tokens":8500}}
{"event_type":"utility","command":"config show","timestamp":"2026-04-20T23:50:00.000Z","success":true}
Use Cases
1. Cost Tracking
Extract token usage for billing and cost analysis:
# Total tokens consumed
jq -s 'map(select(.token_usage != null)) |
map(.token_usage | .input_tokens + .output_tokens) |
add' transaction.jsonl
2. Usage Analytics
Count successful vs. failed workflows:
# Success rate
jq -s 'group_by(.success) |
map({success: .[0].success, count: length})' transaction.jsonl
3. Performance Metrics
Calculate average workflow duration:
# Average duration in seconds
jq -s 'map(select(.end_time != null)) |
map(((.end_time | fromdateiso8601) -
(.start_time | fromdateiso8601))) |
add / length' transaction.jsonl
4. Command Usage
Most frequently used workflows:
# Top 5 commands
jq -s 'group_by(.command) |
map({command: .[0].command, count: length}) |
sort_by(.count) |
reverse |
.[0:5]' transaction.jsonl
5. Model Usage Analysis
Track which AI models are being used and their success rates:
# Count workflows by model (excluding sentinel values)
jq -s 'map(select(.model != null and .model != "not set")) |
group_by(.model) |
map({model: .[0].model, count: length})' transaction.jsonl
# Success rate by model
jq -s 'map(select(.model != null and .model != "not set" and .success != null)) |
group_by(.model) |
map({
model: .[0].model,
total: length,
successful: map(select(.success == true)) | length
}) |
map({
model: .model,
success_rate: ((.successful / .total) * 100 | round)
})' transaction.jsonl
6. Custom Agent Usage
Identify which custom agents are most frequently used:
# Custom agent usage (excluding sentinel values)
jq -s 'map(select(.custom_agent != null and .custom_agent != "none" and .custom_agent != "not set")) |
group_by(.custom_agent) |
map({agent: .[0].custom_agent, count: length}) |
sort_by(.count) |
reverse' transaction.jsonl
7. Cost Tracking by Model
Calculate token usage per model for cost analysis:
# Token usage by model
jq -s 'map(select(.model != null and .model != "not set" and .token_usage != null)) |
group_by(.model) |
map({
model: .[0].model,
total_input: map(.token_usage.input_tokens) | add,
total_output: map(.token_usage.output_tokens) | add
})' transaction.jsonl
Backward Compatibility
The transaction log format is designed for backward compatibility:
- New fields are added with default behavior for missing values
- Existing fields maintain their schema and semantics
- Old log parsers continue to work (they ignore unknown fields)
- Sentinel values (since v2.12.0): The
model,role, andcustom_agentfields use sentinel strings ("not set","none") instead of omitting fields or usingnull. Old logs without these fields can still be read.
Agent Configuration Fields (since v2.12.0):
- Old logs (before v2.12.0) don’t have
model,role, orcustom_agentfields - New logs always include these fields with either real values or sentinel strings
- When parsing old logs, treat missing fields as if they contain
"not set" - When querying logs, filter out sentinel values (
"not set","none") to get real data
Example: The token_usage field was added in version 2.11.0. Logs from older versions don’t have this field, and parsers handle its absence gracefully. Similarly, agent configuration fields are optional when deserializing but always present when serializing.
Best Practices
-
Parse line-by-line: JSONL files can be very large. Process them as a stream rather than loading all into memory.
-
Handle missing fields: Always check for field existence before accessing:
if (event.token_usage) { const tokens = event.token_usage.input_tokens; } -
Filter by event type: Use
event_typefield to process only relevant events:jq 'select(.event_type == "workflow")' transaction.jsonl -
Aggregate across jobs: For organization-wide analytics, collect transaction logs from multiple job directories.
See Also
- Progress Tracking - Real-time workflow progress
- Command Reference - Full command documentation
- OpenSearch Integration - Centralized analytics and monitoring