Skip to content
API ReferenceDoc

CI Runs API

Trigger Certyn runs and poll them, or push your own test results in.

GitHub Actions

For GitHub Actions, use certyn-one/action@v1. Use these endpoints directly for GitLab CI, Jenkins, CircleCI, Azure DevOps, Bitbucket Pipelines, and other systems.

These endpoints are for systems that need direct HTTP control. They cover both directions:

EndpointDirectionScope
POST /api/ci/runsAsk Certyn to run a suiteci:trigger
GET /api/ci/runs/:runIdPoll itci:status
POST /api/ci/runs/:runId/cancelStop itci:cancel
POST /api/ci/resultsPush your own test report inci:results

Create run

POST/api/ci/runsAPI Key

Headers

HeaderValue
Content-Typeapplication/json
X-API-Keyyour API key
Idempotency-Keyrecommended

Body

{
  "projectSlug": "my-app",
  "environmentKey": "staging",
  "processSlug": "smoke-suite",
  "repository": "org/repo",
  "ref": "main",
  "commitSha": "a1b2c3d4",
  "event": "push",
  "externalUrl": "https://ci.example.com/build/123"
}

Notes:

  • projectSlug is required.
  • environmentKey is optional; if omitted, the default project environment is used.
  • Specify exactly one of:
    • processSlug (recommended), or
    • tags (non-empty array)
  • Use Idempotency-Key to prevent duplicate runs on retried CI jobs.

Example request

CERTYN_API_URL="https://api.certyn.io"

curl -sS -X POST "$CERTYN_API_URL/api/ci/runs" \
  -H "Content-Type: application/json" \
  -H "X-API-Key: $CERTYN_API_KEY" \
  -H "Idempotency-Key: $CI_JOB_ID" \
  -d '{
    "projectSlug": "my-app",
    "environmentKey": "staging",
    "processSlug": "smoke-suite",
    "commitSha": "a1b2c3d4"
  }'

Get run status

GET/api/ci/runs/{runId}API Key

Status fields used for gating

  • state (in_progress, completed, cancelled)
  • failed
  • blocked
  • appUrl
  • retryAfterSeconds

Polling guidance

  • Poll every 10-30 seconds.
  • Prefer retryAfterSeconds when returned.
  • Gate fail condition: failed > 0 || blocked > 0.

Example polling loop

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

CERTYN_API_URL="${CERTYN_API_URL:-https://api.certyn.io}"
RUN_ID="${1:?Usage: $0 <run-id>}"
MAX_ATTEMPTS=60

for i in $(seq 1 "$MAX_ATTEMPTS"); do
  resp="$(curl -sS -H "X-API-Key: $CERTYN_API_KEY" "$CERTYN_API_URL/api/ci/runs/$RUN_ID")"
  state="$(echo "$resp" | jq -r '.state')"
  failed="$(echo "$resp" | jq -r '.failed')"
  blocked="$(echo "$resp" | jq -r '.blocked')"
  interval="$(echo "$resp" | jq -r '.retryAfterSeconds // 15')"

  echo "poll=$i state=$state failed=$failed blocked=$blocked"

  if [ "$state" = "completed" ] || [ "$state" = "cancelled" ]; then
    if [ "$failed" != "0" ] || [ "$blocked" != "0" ]; then
      exit 1
    fi
    exit 0
  fi

  sleep "$interval"
done

echo "Timed out waiting for run completion"
exit 1

Cancel run

POST/api/ci/runs/{runId}/cancelAPI Key

Optional JSON body:

{
  "reason": "Cancelled by pipeline"
}

Push results

POST/api/ci/resultsAPI Key

Uploads a report from a suite your CI ran. Certyn ingests it as an already-finished run, so it appears next to the runs Certyn started, with pass and fail history per test. Nothing is executed by an agent.

For the framework-by-framework recipes, see Bring Your Test Framework.

Headers

HeaderValue
X-API-Keyan API key with the ci:results scope
Idempotency-Keyrecommended; a replayed key returns the original run with Idempotency-Replayed: true

Query parameters

The report is the raw request body. Everything else is a query parameter.

ParameterRequiredNotes
projectSlugyes
suiteyesStable slug for this suite, for example e2e. A process is created for it on first push and reused after.
suiteNamenoDisplay name for that process
environmentKeynoDefaults to the project's default environment
formatnoplaywright or junit. Sniffed from the body when omitted: a leading < means JUnit XML, anything else means Playwright JSON. Passing it explicitly is still recommended.
repositorynoFor example org/repo
refnoBranch or tag
commitShano
commitMessageno
eventnoFor example push or pull_request
externalUrlnoLink back to the CI job

Body limit is 32 MB.

Example request

CERTYN_API_URL="https://api.certyn.io"

curl -sS -X POST \
  "$CERTYN_API_URL/api/ci/results?projectSlug=my-app&suite=e2e&environmentKey=staging&format=junit" \
  -H "X-API-Key: $CERTYN_API_KEY" \
  -H "Idempotency-Key: $CI_JOB_ID" \
  -H "Content-Type: application/xml" \
  --data-binary @junit.xml

Response

201 Created on first ingest, 200 OK on an idempotent replay.

{
  "runId": "run-id",
  "processSlug": "e2e",
  "total": 128,
  "passed": 124,
  "failed": 3,
  "skipped": 1,
  "hasFailures": true,
  "statusPath": "/api/ci/runs/run-id",
  "statusUrl": "https://api.certyn.io/api/ci/runs/run-id",
  "appUrl": "https://app.certyn.io/...",
  "message": null
}

Gate on hasFailures, or on failed if you want to allow a threshold. The statusUrl is the same run status endpoint documented above.

Errors

StatusWhen
400Missing projectSlug or suite, empty body, unknown format, or a report Certyn could not parse
404Project or environment not found
409The suite slug already belongs to a process of a different kind, or the idempotency key was reused with a different report