Skip to main content

Extracting Change Data

Overmind's change analysis results are available as structured JSON, making it straightforward to build custom integrations with your existing tools — whether that's gating deployments, feeding dashboards, posting to Slack, or building compliance audit trails.

This guide covers how to retrieve the JSON output and extract the data you need using jq.

Getting the Results

Using the CLI

# Submit a plan and get the change URL
CHANGE_URL=$(overmind changes submit-plan tfplan.json \
--ticket-link "https://github.com/org/repo/pull/123")

# Get results as JSON (pass the URL directly with --change)
overmind changes get-change \
--change "$CHANGE_URL" \
--format json > change-results.json
info

The submit-plan command outputs the change URL to stdout. Plan files are positional arguments, not flags. The get-change command accepts either --change <url> or --uuid <uuid>.

In CI/CD Pipelines

# GitHub Actions example
- name: Submit to Overmind
id: overmind
run: |
CHANGE_URL=$(overmind changes submit-plan tfplan.json \
--ticket-link "${{ github.server_url }}/${{ github.repository }}/pull/${{ github.event.pull_request.number }}")
echo "change_url=$CHANGE_URL" >> $GITHUB_OUTPUT

- name: Get Results
run: |
overmind changes get-change \
--change "${{ steps.overmind.outputs.change_url }}" \
--format json > change-results.json

JSON Structure

The JSON output contains four top-level objects:

FieldTypeDescription
changeobjectMetadata about the change and blast radius
risksarrayList of identified risks
hypothesesarrayInvestigation hypotheses
signalsobjectSignal data (optional)

Risks

Each risk has a severity level and details about what was found:

{
"title": "Opening SSH to 0.0.0.0/0 exposes instances to internet",
"severity": "SEVERITY_HIGH",
"description": "This change adds an ingress rule allowing SSH from anywhere...",
"relatedItems": [{"type": "aws.ec2-instance", "uniqueAttributeValue": "web-1"}]
}
FieldTypeValues
severitystringSEVERITY_LOW, SEVERITY_MEDIUM, SEVERITY_HIGH, SEVERITY_UNSPECIFIED
titlestringShort summary of the risk
descriptionstringDetailed explanation
relatedItemsarrayReferences to affected resources

Change Metadata

Blast radius metrics live under .change.metadata:

FieldDescription
numAffectedItemsNumber of resources in the blast radius
numAffectedEdgesNumber of relationships between resources
total_observationsTotal evidence points gathered during analysis

Hypotheses

Overmind forms hypotheses about potential risks and investigates them:

FieldTypeValues
statusstringINVESTIGATED_HYPOTHESIS_STATUS_FORMING, INVESTIGATED_HYPOTHESIS_STATUS_INVESTIGATING, INVESTIGATED_HYPOTHESIS_STATUS_PROVEN, INVESTIGATED_HYPOTHESIS_STATUS_DISPROVEN, INVESTIGATED_HYPOTHESIS_STATUS_UNSPECIFIED
titlestringWhat was investigated
numObservationsnumberEvidence gathered

Extracting Data with jq

Basic Metrics

# Count total risks
jq '.risks | length' change-results.json

# Count by severity
jq '[.risks[] | select(.severity == "SEVERITY_HIGH")] | length' change-results.json
jq '[.risks[] | select(.severity == "SEVERITY_MEDIUM")] | length' change-results.json
jq '[.risks[] | select(.severity == "SEVERITY_LOW")] | length' change-results.json

# Get blast radius size
jq '.change.metadata.numAffectedItems' change-results.json
jq '.change.metadata.numAffectedEdges' change-results.json

# Count hypotheses
jq '.hypotheses | length' change-results.json

Extract Risk Details

# Get all risk titles and severities
jq '.risks[] | {title, severity}' change-results.json

# Get high-severity risks only
jq '.risks[] | select(.severity == "SEVERITY_HIGH") | {title, description}' change-results.json

# Extract as compact JSON array
jq -c '[.risks[] | {title: .title, severity: .severity, description: .description}]' change-results.json

# Get related resources for each risk
jq '.risks[] | {title, relatedItems}' change-results.json

CI/CD Integration Examples

Fail on High Risks

- name: Check for High Risks
run: |
HIGH_RISKS=$(jq '[.risks[] | select(.severity == "SEVERITY_HIGH")] | length' change-results.json)

if [ "$HIGH_RISKS" -gt 0 ]; then
echo "::error::Found $HIGH_RISKS high-severity risks"
jq -r '.risks[] | select(.severity == "SEVERITY_HIGH") | " - \(.title)"' change-results.json
exit 1
fi

echo "No high-severity risks found"

GitLab CI

analyze:
script:
- |
CHANGE_URL=$(overmind changes submit-plan tfplan.json --ticket-link "$CI_MERGE_REQUEST_URL")
overmind changes get-change --change "$CHANGE_URL" --format json > results.json
- |
HIGH_RISKS=$(jq '[.risks[] | select(.severity == "SEVERITY_HIGH")] | length' results.json)
if [ "$HIGH_RISKS" -gt 0 ]; then
echo "Found $HIGH_RISKS high-severity risks"
exit 1
fi
artifacts:
paths:
- results.json

Building a Dashboard

Posting to a Webhook

# Extract metrics and POST to your dashboard
PAYLOAD=$(jq -c '{
timestamp: now | todate,
risks: {
total: (.risks | length),
high: ([.risks[] | select(.severity == "SEVERITY_HIGH")] | length),
medium: ([.risks[] | select(.severity == "SEVERITY_MEDIUM")] | length),
low: ([.risks[] | select(.severity == "SEVERITY_LOW")] | length)
},
blastRadius: {
nodes: .change.metadata.numAffectedItems,
edges: .change.metadata.numAffectedEdges
},
hypotheses: (.hypotheses | length)
}' change-results.json)

curl -X POST "https://your-dashboard.com/api/results" \
-H "Content-Type: application/json" \
-H "Authorization: Bearer $API_KEY" \
-d "$PAYLOAD"

Tracking Metrics Over Time

# Append to CSV for each run
echo "$(date -Iseconds),\
$(jq '.change.metadata.numAffectedItems' change-results.json),\
$(jq '.change.metadata.numAffectedEdges' change-results.json),\
$(jq '.risks | length' change-results.json),\
$(jq '[.risks[] | select(.severity == "SEVERITY_HIGH")] | length' change-results.json)" \
>> overmind-metrics.csv