All docs

    Manta handbook · 11 of 12

    How to Trigger Manta 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 Manta AI Team4 min read

    By the end of this guide, your pipeline will start a Manta 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. If your organization is on a free plan, the endpoint returns /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 (MANTA_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 Authorization: Bearer <key> (or X-Api-Key: <key>). It returns immediately with 202 and a run id to poll. Swap <your-manta-api-host> below for your Manta API base URL — the same host your dashboard talks to.

    Exploration (full or diff crawl):

    curl -X POST https://<your-manta-api-host>/v1/ci/runs \
      -H "Authorization: Bearer $MANTA_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://<your-manta-api-host>/v1/ci/runs \
      -H "Authorization: Bearer $MANTA_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, not a second run) instead of starting a duplicate.

    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://<your-manta-api-host>/v1/ci/runs/$RUN_ID \
      -H "Authorization: Bearer $MANTA_API_KEY"
    

    For a test-execution run, the response also carries a tests object (total, passed, failed, errored, pending) and a reportUrl deep link into the dashboard. 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.

    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 MANTA_API_KEY as a repository secret, then:

    - name: Manta test gate
      env:
        MANTA_API_KEY: ${{ secrets.MANTA_API_KEY }}
      run: |
        RUN_ID=$(curl -sf -X POST https://<your-manta-api-host>/v1/ci/runs \
          -H "Authorization: Bearer $MANTA_API_KEY" -H "Content-Type: application/json" \
          -d '{"projectId":"'"$MANTA_PROJECT_ID"'","environmentId":"'"$MANTA_ENVIRONMENT_ID"'",
               "type":"TEST_EXECUTION","testSuiteId":"'"$MANTA_SUITE_ID"'",
               "idempotencyKey":"'"$GITHUB_SHA"'"}' | jq -r .runId)
    
        while :; do
          STATUS=$(curl -sf https://<your-manta-api-host>/v1/ci/runs/$RUN_ID \
            -H "Authorization: Bearer $MANTA_API_KEY" | jq -r .status)
          case $STATUS in
            PASSED) echo "Manta tests passed"; exit 0 ;;
            FAILED|TIMEOUT|CANCELLED) echo "Manta tests did not pass"; exit 1 ;;
            *) sleep 15 ;;
          esac
        done
    

    GitLab CI

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

    manta_test_gate:
      stage: test
      image: alpine:latest
      before_script:
        - apk add --no-cache curl jq
      script:
        - |
          RUN_ID=$(curl -sf -X POST https://<your-manta-api-host>/v1/ci/runs \
            -H "Authorization: Bearer $MANTA_API_KEY" -H "Content-Type: application/json" \
            -d '{"projectId":"'"$MANTA_PROJECT_ID"'","environmentId":"'"$MANTA_ENVIRONMENT_ID"'",
                 "type":"TEST_EXECUTION","testSuiteId":"'"$MANTA_SUITE_ID"'",
                 "idempotencyKey":"'"$CI_COMMIT_SHA"'"}' | jq -r .runId)
    
          while :; do
            STATUS=$(curl -sf https://<your-manta-api-host>/v1/ci/runs/$RUN_ID \
              -H "Authorization: Bearer $MANTA_API_KEY" | jq -r .status)
            case $STATUS in
              PASSED) echo "Manta tests passed"; exit 0 ;;
              FAILED|TIMEOUT|CANCELLED) echo "Manta 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 Manta's cloud runners, add "executionEnv": "LOCAL" and "platform" (LINUX, MAC_SILICON, MAC_INTEL, or WINDOWS) to the trigger body. The response includes a package with a downloadUrl and a short-lived runKey — your pipeline downloads the runner, executes it inside your network, and gates on its exit code. This is the CI equivalent of a private-environment Runner.

    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 fails.
    • Match run depth to the moment, the same as any other trigger — see fitting Manta 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 Manta at a URL and see what it finds — no scripts, no setup. Free, no credit card.