Quick start

Create a project and send your first result from CI.

You send us three numbers after each test run: how many passed, how many failed, and the total. We keep the latest result for your project and serve a badge and a public verification page from it. Setup takes about five minutes.

1. Create a project

POST/v1/projects

Use the email address you will sign in with. We use it to link the project to your account later.

curl
curl -X POST https://api.virdify.com/v1/projects \
  -H "Content-Type: application/json" \
  -d '{"email":"you@example.com","name":"Checkout suite"}'

The response contains write_key, badge_url and verify_url.

Copy the write key now

We show it once and store only a hash of it, so we cannot recover it. If you lose it, sign in to the dashboard and rotate it.

2. Store the key as a CI secret

Save it as BADGER_WRITE_KEY in your CI provider's secret store. Never commit it.

3. Send the result

POST/v1/results

Send the result after the tests run, whether they pass or fail. Always send a run_id that is unique per run: if your job retries the request, we count the run once.

This example reads the totals from a Jest JSON report. Replace the three jq lookups with the counts from your own test runner.

Workflow steps
- name: Run tests
  run: npx jest --json --outputFile=report.json || echo "TEST_EXIT=$?" >> "$GITHUB_ENV"

- name: Report results to Badger
  if: always()
  env:
    BADGER_WRITE_KEY: ${{ secrets.BADGER_WRITE_KEY }}
  run: |
    jq -n \
      --argjson passed "$(jq .numPassedTests report.json)" \
      --argjson failed "$(jq .numFailedTests report.json)" \
      --argjson total  "$(jq .numTotalTests report.json)" \
      --arg run_id "${{ github.run_id }}-${{ github.run_attempt }}" \
      --arg branch "${{ github.ref_name }}" \
      --arg commit "${{ github.sha }}" \
      '{passed:$passed, failed:$failed, total:$total, run_id:$run_id, branch:$branch, commit:$commit}' |
    curl --fail-with-body -sS -X POST "https://api.virdify.com/v1/results" \
      -H "Authorization: Bearer $BADGER_WRITE_KEY" \
      -H "Content-Type: application/json" \
      --data @-

- name: Fail the job if tests failed
  if: env.TEST_EXIT != ''
  run: exit "$TEST_EXIT"
.gitlab-ci.yml
tests:
  image: node:22
  before_script:
    - apt-get update -qq && apt-get install -y -qq jq curl
  script:
    - npx jest --json --outputFile=report.json || TEST_EXIT=$?
    - |
      jq -n \
        --argjson passed "$(jq .numPassedTests report.json)" \
        --argjson failed "$(jq .numFailedTests report.json)" \
        --argjson total  "$(jq .numTotalTests report.json)" \
        --arg run_id "$CI_PIPELINE_ID-$CI_JOB_ID" \
        --arg branch "$CI_COMMIT_REF_NAME" \
        --arg commit "$CI_COMMIT_SHA" \
        '{passed:$passed, failed:$failed, total:$total, run_id:$run_id, branch:$branch, commit:$commit}' |
      curl --fail-with-body -sS -X POST "https://api.virdify.com/v1/results" \
        -H "Authorization: Bearer $BADGER_WRITE_KEY" \
        -H "Content-Type: application/json" \
        --data @-
    - exit ${TEST_EXIT:-0}
curl
curl -X POST https://api.virdify.com/v1/results \
  -H "Authorization: Bearer $BADGER_WRITE_KEY" \
  -H "Content-Type: application/json" \
  -d '{"passed":142,"failed":3,"total":145,"run_id":"build-1042","branch":"main","commit":"3f2a9c1"}'

A successful call returns 200 with {"status":"stored","duplicate":false,...}. Send the same run_id again and you get 200 with "duplicate":true.

Rules for the numbers

  • total is at least 1.
  • passed + failed must not exceed total. The difference counts as skipped or not run.
  • The request body is 1 KB at most.

Next, add the badge to your README.