Blog
Workflows

Automating Schematic Review in a KiCad Workflow

Gilad Shapira 7 min read
Automating Schematic Review in a KiCad Workflow

When your team's schematic review process depends on someone remembering to run it, it will be skipped on the week a deadline is close. Automated review that fires on every push to your version control system does not skip. This post walks through how to wire CADY into a KiCad workflow using the REST API, from netlist export through violation report delivery, with a shell script you can adapt for your CI setup.

The goal is a check that runs without human initiation, returns results in a format that can block a merge or post to a team channel, and does not require an engineer to log into a web UI each time. CADY does not make workflow decisions for you. It reviews the netlist and reports what it finds. What you do with that report is your team's policy. If you are using Altium rather than KiCad, the same API approach applies with IPC-2581 export instead; see Integrating CADY into an Altium Workflow for format-specific details.

KiCad netlist export formats

KiCad 6 and later support several netlist export formats from the schematic editor. For CADY, the most reliable path is the native KiCad netlist format (.net), which captures component attributes, pin-to-net mappings, and hierarchy references cleanly. The legacy Orcad-compatible format is also supported. You can export manually via File > Export > Netlist, but for automation you want a headless path.

KiCad's command-line scripting interface (the kicad-cli binary, available from KiCad 7.0 onward) supports headless schematic export. The relevant command is:

kicad-cli sch export netlist \
  --output project.net \
  --format kicadxml \
  project.kicad_sch

This runs cleanly in a Linux CI container. On macOS, you need to reference the CLI binary inside the KiCad.app bundle at /Applications/KiCad/KiCad.app/Contents/MacOS/kicad-cli. On Windows, the binary is in the KiCad installation directory; invoking it from a Git Bash or WSL environment requires adding it to PATH.

The output is an XML file describing the full netlist. CADY accepts this format directly. You can also export BOM data from the same CLI invocation if you want CADY's BOM consistency checks to run in the same pass:

kicad-cli sch export bom \
  --output project.csv \
  --fields "Reference,Value,Footprint,MPN,Manufacturer" \
  project.kicad_sch

Submitting to CADY via the API

The CADY API accepts netlist and BOM files as a multipart form upload to POST /reviews. Authentication is via API key in the X-CADY-Key header. The response body contains a review ID, which you then poll with GET /reviews/{id} until the status transitions from pending to complete.

Here is a complete shell script that runs the upload, polls for completion, and exits with a non-zero code if any CRIT violations are present:

#!/usr/bin/env bash
set -euo pipefail

CADY_API_KEY="${CADY_API_KEY}"
CADY_API_BASE="https://api.cadysoiutions.com/v1"
NETLIST_FILE="${1:-project.net}"
BOM_FILE="${2:-}"

# Upload
UPLOAD_ARGS=(-s -X POST "${CADY_API_BASE}/reviews" \
  -H "X-CADY-Key: ${CADY_API_KEY}" \
  -F "netlist=@${NETLIST_FILE}")

if [[ -n "${BOM_FILE}" ]]; then
  UPLOAD_ARGS+=(-F "bom=@${BOM_FILE}")
fi

REVIEW_JSON=$(curl "${UPLOAD_ARGS[@]}")
REVIEW_ID=$(echo "${REVIEW_JSON}" | python3 -c "import sys,json; print(json.load(sys.stdin)['id'])")

echo "Review submitted: ${REVIEW_ID}"

# Poll until complete (max 3 minutes)
for i in $(seq 1 36); do
  sleep 5
  STATUS_JSON=$(curl -s "${CADY_API_BASE}/reviews/${REVIEW_ID}" \
    -H "X-CADY-Key: ${CADY_API_KEY}")
  STATUS=$(echo "${STATUS_JSON}" | python3 -c "import sys,json; print(json.load(sys.stdin)['status'])")
  if [[ "${STATUS}" == "complete" ]]; then
    break
  fi
  echo "  waiting... (${i}/36)"
done

if [[ "${STATUS}" != "complete" ]]; then
  echo "ERROR: review timed out"
  exit 2
fi

# Count CRIT violations and exit accordingly
CRIT_COUNT=$(echo "${STATUS_JSON}" | python3 -c \
  "import sys,json; d=json.load(sys.stdin); print(sum(1 for v in d['violations'] if v['severity']=='CRIT'))")

echo "Violations: CRIT=${CRIT_COUNT}"
echo "Report URL: $(echo ${STATUS_JSON} | python3 -c \"import sys,json; print(json.load(sys.stdin)['report_url'])\")"

if [[ "${CRIT_COUNT}" -gt 0 ]]; then
  exit 1
fi
exit 0

Set CADY_API_KEY as a CI secret. In GitHub Actions, that goes into repository secrets and is injected via env: in your workflow YAML. In GitLab CI, it is a masked CI variable.

Integrating into a CI pipeline

The script above exits 1 on CRIT violations. If you wire it into a CI job that runs on pull request creation and push to main, you get a blocking check that prevents merge when critical schematic errors are present.

A minimal GitHub Actions workflow looks like this:

name: Schematic Review
on:
  pull_request:
    paths:
      - '**.kicad_sch'
      - '**.kicad_pro'

jobs:
  cady-review:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - name: Install KiCad CLI
        run: |
          sudo add-apt-repository ppa:kicad/kicad-7.0-releases
          sudo apt-get update -q
          sudo apt-get install -y kicad

      - name: Export netlist
        run: |
          kicad-cli sch export netlist \
            --output schematic.net \
            --format kicadxml \
            hardware/project.kicad_sch

      - name: Export BOM
        run: |
          kicad-cli sch export bom \
            --output bom.csv \
            --fields "Reference,Value,Footprint,MPN,Manufacturer" \
            hardware/project.kicad_sch

      - name: Run CADY review
        env:
          CADY_API_KEY: ${{ secrets.CADY_API_KEY }}
        run: bash scripts/cady-review.sh schematic.net bom.csv

The paths filter means the job only runs when schematic files change. There is no reason to run a schematic review on a commit that only changes firmware source.

Handling WARN violations and team policies

Not every team wants to block on WARN violations. The script above exits 1 only on CRIT. You can extend it to count WARNs and write them to a GitHub step summary or post them to a Slack webhook without blocking the merge. The JSON response structure is the same for all severity levels.

One pattern that has worked well in practice: CRIT violations block the PR, WARN violations post as a comment on the PR with a link to the full report, and INFO violations are logged to the artifact store for post-merge review. This lets engineers decide whether a WARN is intentional before the board goes out, without forcing a conversation for every advisory finding.

To be clear about what CADY does not do in this pipeline: it does not auto-correct your schematic. It does not push suggested edits back to KiCad. It finds violations, describes them with enough context to act on, and reports the result. The repair is yours to make in the EDA tool.

Caching and rate limits

The Pro tier's API quota is 1,000 calls per month. For a team pushing multiple times per day, you can reduce API consumption by computing a hash of the netlist file and skipping the CADY call if the netlist has not changed since the last successful review. The review ID from the previous run can be stored in a CI artifact and the report URL reused in the PR comment. This is worth setting up for teams with active schematic iteration.

On the Team tier, API access is unlimited, so the caching optimization matters less. The more important consideration at that tier is parallel jobs: if two PRs run the CADY review simultaneously, both will succeed without interfering with each other. Reviews are scoped to the submitted netlist file, not to a shared project state.

What this does not replace

Automated review catches rule-based violations: missing caps, undriven nets, BOM mismatches, clearance violations. It does not catch intent errors, such as a power rail labeled 3.3V that actually runs at 1.8V because the regulator input configuration changed, or a bus topology that is electrically correct but will not meet timing at the chosen MCU's operating frequency. Those catches still need a senior engineer's eyes on the design. The automated check is the tier below, making sure the obvious mechanical errors are out of the way before that review happens.

More from the blog