All docs

    BlackBat AI handbook · 11 of 13

    How to Trigger BlackBat AI from Your CI/CD Pipeline

    Call the CI/CD API from GitHub Actions, GitLab CI, or any pipeline to start an exploration or test run and gate a deploy on the result.

    Last updated: September 16, 2026
    by BlackBat AI Team7 min read

    By the end of this guide, your pipeline will start a BlackBat AI run on its own — on a merge, before a release, whenever you say — and your deploy step will wait on the result instead of someone checking the dashboard by hand.

    CI/CD triggering is available on paid plans. The free plan and trials aren't included: the trigger endpoint answers 403 with /billing/feature-not-available/ci-triggers until you upgrade.

    Step 1 — Create an API key

    From Settings → CI/CD (OWNER or ADMIN only), create a key and give it a name that says where it's used, e.g. "GitHub Actions – web". The key is shown once — copy it straight into your CI provider's secrets (BLACKBAT_API_KEY or similar). If you lose it, revoke it and create a new one; there's no way to view it again.

    [Screenshot: the CI/CD settings page with a newly generated key]

    By default (Notifications: Off), runs triggered by a key notify no one — your pipeline is expected to read the result from the API. Set a key's Notifications to Failures only to notify people only about failed, timed-out or cancelled runs, or to All outcomes to notify about every run. Each person's own notification preferences still apply on top of this.

    Step 2 — Trigger a run

    POST /v1/ci/runs, authenticated with your key, sent as an x-api-key: <key> header (or as Authorization: Bearer <key> if you prefer). It returns immediately with 202 and a run id to poll; every other endpoint below takes the key the same way. The examples below use the production API at https://api.blackbat.dev.

    Exploration (full or diff crawl):

    curl -X POST https://api.blackbat.dev/v1/ci/runs \
      -H "x-api-key: $BLACKBAT_API_KEY" \
      -H "Content-Type: application/json" \
      -d '{
            "projectId": "<project-id>",
            "environmentId": "<environment-id>",
            "type": "CRAWL",
            "crawlingMode": "DIFF",
            "idempotencyKey": "'"$GITHUB_SHA"'"
          }'
    

    A DIFF crawl needs a prior full crawl on the environment to diff against — run a FULL crawl first if this is a new environment.

    Test execution (a suite or a single plan):

    curl -X POST https://api.blackbat.dev/v1/ci/runs \
      -H "x-api-key: $BLACKBAT_API_KEY" \
      -H "Content-Type: application/json" \
      -d '{
            "projectId": "<project-id>",
            "environmentId": "<environment-id>",
            "type": "TEST_EXECUTION",
            "testSuiteId": "<test-suite-id>",
            "idempotencyKey": "'"$GITHUB_SHA"'"
          }'
    

    Provide exactly one of testSuiteId or testPlanId. projectId and environmentId are visible on the project/environment pages in the dashboard.

    The idempotencyKey is optional but worth setting to something stable for the trigger, like the commit SHA — if a retry sends the same key again, you get the original run back (200 with idempotentReplay: true, not a second run) instead of starting a duplicate. You can also set externalRef (a branch, commit or pipeline URL) and it's shown on the run.

    Optional settings

    Beyond the fields above, a trigger can set the same limits and options as a run started from the dashboard:

    • Limits (crawls): maxPages, maxDepth, maxPageTemplates, templateSampleSize (1–30, default 3), maxCredits, maxRunTime (minutes, up to 1440), crawlWorkers (1–10) and allowSubdomainCrawling.
    • Access to the site: loginInstructions, registrationInstructions, paymentInstructions, oauthAllowedIdpOrigins, proxyServer (with proxyUsername / proxyPassword) and httpAuthUsername / httpAuthPassword. These are saved on the environment the first time they're sent, the same as a manual run.
    • Site documentation (crawls only): generateDocumentation, generateUserStories (needs generateDocumentation) and a required intelligenceMaxCredits spend limit, in credits as shown in the dashboard. Pick the limit yourself; the dashboard shows a cost estimate when you start documentation by hand. The work runs as a separate billed job after the crawl, stops at the limit and keeps what it finished, and the person who created the API key is notified when it ends. This needs a plan that includes site documentation, otherwise the call returns 403 with /billing/feature-not-available/site-intelligence. A DIFF crawl can send these only when the crawl it compares with has fully documented the site (otherwise 400, /validation/not-allowed/intelligence-without-current-documentation), and then only changed pages and sections are paid for. Sending them on a test execution is a 400 (/validation/not-allowed/intelligence-on-this-run).
    • Active security probes (crawls only): activeProbes, loginProbes, bruteForceProbe, unauthReplayMaxEndpoints and activeProbeBudgetMs. Turning a probe on needs consent for the environment: an Owner or Admin must enable active security testing for it in Settings → Environments. Without consent no probe runs, and sending one as true returns 400 (/validation/consent-required/activeProbes). Probe settings on anything other than a crawl are also a 400.

    Step 3 — Poll for the result — your deploy gate

    GET /v1/ci/runs/:id returns the current status:

    • QUEUED / RUNNING / PROCESSING — still going, keep polling.
    • PASSED — proceed with the deploy.
    • FAILED / TIMEOUT / CANCELLED — block the deploy.
    curl https://api.blackbat.dev/v1/ci/runs/$RUN_ID \
      -H "x-api-key: $BLACKBAT_API_KEY"
    

    Every response also has creditsSpent, bugCount, startedAt / endedAt, and a reportUrl deep link into the dashboard. For a test-execution run it carries a tests object (total, passed, failed, errored, pending). status goes terminal as soon as the run itself is done, so the gate never sits waiting on the AI write-up to finish generating.

    For per-test detail in your pipeline log — which test failed and why — call GET /v1/ci/runs/:id/results. It returns one entry per executed test plan with its status (PASSED, FAILED, ERROR or PENDING), the failure reason, per-step traces (including any self-healing) and a written interpretation. It's an empty list for exploration runs.

    Step 4 — Wire it into your pipeline

    The same trigger-then-poll shape works anywhere curl and jq are available — here it is as a GitHub Actions step and a GitLab CI job.

    GitHub Actions

    Add BLACKBAT_API_KEY as a repository secret, then:

    - name: BlackBat AI test gate
      env:
        BLACKBAT_API_KEY: ${{ secrets.BLACKBAT_API_KEY }}
      run: |
        RUN_ID=$(curl -sf -X POST https://api.blackbat.dev/v1/ci/runs \
          -H "x-api-key: $BLACKBAT_API_KEY" -H "Content-Type: application/json" \
          -d '{"projectId":"'"$BLACKBAT_PROJECT_ID"'","environmentId":"'"$BLACKBAT_ENVIRONMENT_ID"'",
               "type":"TEST_EXECUTION","testSuiteId":"'"$BLACKBAT_SUITE_ID"'",
               "idempotencyKey":"'"$GITHUB_SHA"'"}' | jq -r .runId)
    
        while :; do
          STATUS=$(curl -sf https://api.blackbat.dev/v1/ci/runs/$RUN_ID \
            -H "x-api-key: $BLACKBAT_API_KEY" | jq -r .status)
          case $STATUS in
            PASSED) echo "BlackBat AI tests passed"; exit 0 ;;
            FAILED|TIMEOUT|CANCELLED) echo "BlackBat AI tests did not pass"; exit 1 ;;
            *) sleep 15 ;;
          esac
        done
    

    GitLab CI

    Add BLACKBAT_API_KEY as a masked CI/CD variable (Settings → CI/CD → Variables), then:

    blackbat_test_gate:
      stage: test
      image: alpine:latest
      before_script:
        - apk add --no-cache curl jq
      script:
        - |
          RUN_ID=$(curl -sf -X POST https://api.blackbat.dev/v1/ci/runs \
            -H "x-api-key: $BLACKBAT_API_KEY" -H "Content-Type: application/json" \
            -d '{"projectId":"'"$BLACKBAT_PROJECT_ID"'","environmentId":"'"$BLACKBAT_ENVIRONMENT_ID"'",
                 "type":"TEST_EXECUTION","testSuiteId":"'"$BLACKBAT_SUITE_ID"'",
                 "idempotencyKey":"'"$CI_COMMIT_SHA"'"}' | jq -r .runId)
    
          while :; do
            STATUS=$(curl -sf https://api.blackbat.dev/v1/ci/runs/$RUN_ID \
              -H "x-api-key: $BLACKBAT_API_KEY" | jq -r .status)
            case $STATUS in
              PASSED) echo "BlackBat AI tests passed"; exit 0 ;;
              FAILED|TIMEOUT|CANCELLED) echo "BlackBat AI tests did not pass"; exit 1 ;;
              *) sleep 15 ;;
            esac
          done
    

    CircleCI, Jenkins, and any other pipeline that can run curl follow the same shape — trigger, poll, exit non-zero on a bad status.

    Running against a private target instead of the cloud

    If the environment isn't reachable from BlackBat AI's cloud runners, add "executionEnv": "LOCAL" and "platform" (LINUX, MAC_SILICON, MAC_INTEL, or WINDOWS) to the trigger body. The 202 response includes a package with a downloadUrl and a short-lived runKey (the key the runner authenticates with, so keep it out of your logs). Your pipeline downloads the runner zip, executes it inside your network, and you poll the run as usual. The zip is also available at GET /v1/ci/runs/:id/package, which only works for runs created with executionEnv: LOCAL and a platform. This is the CI equivalent of a private-environment Runner.

    API specification

    The full request and response schemas are available as an OpenAPI 3 file: download blackbat-ci.yaml. Import it into Postman, Insomnia or a client generator, or hand it to your AI coding assistant when it writes the pipeline step.

    Errors and rate limits

    | Status | Meaning | |---|---| | 400 | The request doesn't describe a valid target: crawlingMode missing on a crawl, not exactly one of testSuiteId / testPlanId on a test execution, platform missing on a local run, or /runs/diff-requires-full-crawl when a DIFF crawl has no baseline. Field problems come back as /validation/<constraint>/<field>. | | 401 | The key is missing, invalid, revoked or expired (/auth/missing-api-key or /auth/invalid-api-key). | | 402 | The organization has no credits left. | | 403 | Your plan doesn't include CI triggering (/billing/feature-not-available/ci-triggers), or the run isn't a local run (/ci/package-not-available, package download only). | | 404 | The project, environment, suite, plan or run isn't in the key's organization. | | 429 | Too many requests for this key (60 per minute by default). |

    Every authenticated response carries X-RateLimit-Limit-ci-trigger, X-RateLimit-Remaining-ci-trigger and X-RateLimit-Reset-ci-trigger (seconds until the window resets), so a pipeline can back off before it's throttled. A 15-second poll interval stays well inside the limit.

    Tips and common mistakes

    • Poll, don't sleep-and-hope. A run's duration depends on what it finds — poll on an interval and act on status, not a fixed timeout.
    • Set idempotencyKey to something tied to the trigger, like the commit SHA. A retried CI step then resumes the same run instead of starting a second one and doubling your credit spend.
    • DIFF crawls need a baseline. If the environment has never had a full crawl, trigger FULL first — a DIFF on a fresh environment is rejected with 400 (/runs/diff-requires-full-crawl).
    • Match run depth to the moment, the same as any other trigger — see fitting BlackBat AI into your release process for what to run on a merge versus before a release.
    • One key per pipeline makes it easy to see which pipeline is spending credits, and to revoke just that one if a secret leaks.

    What to try next

    Generate an API key and add the trigger step to your pipeline.

    Try it on your own app

    Point BlackBat AI at a URL and see what it finds — no scripts, no setup. Free, no credit card.