API v1.1

Vývojářské API

REST API pro CI: spouštějte scany, blokujte pull requesty při regresích dataLayer a compliance a načítejte výsledky přímo do své pipeline.

Autentizace

Každý požadavek nese projektový API klíč vytvořený v Nastavení projektu → API klíče. Posílejte jej jako Bearer token nebo hlavičku X-API-Key. Každý klíč má scope; celý klíč se zobrazí pouze jednou při vytvoření.

# Preferred
Authorization: Bearer ap_live_xxxxxxxxxxxxxxxx
# Alternative
X-API-Key: ap_live_xxxxxxxxxxxxxxxx

Scopy

ScopePopis
scan:readList targets, journeys, and scan history; poll a scan and evaluate a CI gate.
scan:writeTrigger ad-hoc scans, re-run saved tests, and cancel a pending/running scan.
upload:writeUpload a CI APK/XAPK artifact for a mobile scan (valid 14 days).

Endpointy

Všechny endpointy vycházejí ze základní URL API a vyžadují uvedený scope.

MetodaEndpointScopePopis
GET/api/v1/scansscan:readList scan history
POST/api/v1/scansscan:writeTrigger an ad-hoc scan
GET/api/v1/scans/{id}scan:readPoll a scan + evaluate a CI gate
DELETE/api/v1/scans/{id}scan:writeCancel a pending/running scan
GET/api/v1/targetsscan:readList saved tests
POST/api/v1/targets/{id}/scansscan:writeRe-run a saved test
GET/api/v1/journeysscan:readList saved journeys
POST/api/v1/uploadsupload:writeUpload a CI APK/XAPK

Možnosti CI brány

Při dotazování na scan předejte URL-zakódovaný JSON jako ?gate=. Predikáty se kombinují pomocí OR — brána selže, pokud vyhoví kterýkoli predikát. CI by mělo skončit nenulovým kódem, když je gate.passed false.

MožnostTypPopis
minComplianceScorenumber (0–100)Fail when the compliance score is below this threshold (score_below_threshold).
failOnNewDataLayerRegressionsbooleanFail on new, unacknowledged dataLayer regressions (datalayer_regressions).
failOnSeverity"info" | "warning" | "critical"Fail when any diff meets or exceeds this severity (severity_exceeded).
failOnBrokenStepbooleanFail when the journey replay broke mid-flow (status_broken_step).
failOnStatus"failed" | "cancelled" | "broken_step"[]Fail when the scan ends in any of the listed statuses (status_<status>).
failOnNewVendorsbooleanFail when a first-seen vendor appears in the monitored dataLayer (new_vendors).
Neblokující upozornění (například brána dataLayer požadovaná pro scan bez baseline) se vracejí v samostatném poli warnings[] — nikdy nepřeklopí gate.passed na false.

Detail diffů (include=diffs)

Přidejte ?include=diffs k dotazu na scan, aby se vložil úplný seznam nevyřešených diffů. Diff je hodnotově slepý: hlásí názvy parametrů a JS typy každé události, nikdy jejich hodnoty.

# Ask for the full unresolved-diff list on a terminal scan:
GET /api/v1/scans/{id}?include=diffs

# Response adds a diffs[] array (value-blind: name + JS-type only):
{
  "status": "completed",
  "gate": { "passed": false, "failures": [ { "code": "new_vendors", "detail": "1 new-vendor diff(s)" } ] },
  "warnings": [],
  "diffs": [
    {
      "stepIndex": 2,
      "elementKey": "event:tiktok::purchase",
      "vendor": "tiktok",
      "diffType": "extra_event",
      "severity": "warning",
      "description": "Event present in the scan but not in the expected schema"
    }
  ]
}
# diffType is one of: missing_event | extra_event | event_drifted | missing_param |
# extra_param | param_type_changed | consent_value_changed. elementKey is
# "event:<vendor>::<name>", "param:<vendor>::<event>::<param>", or
# "consent:<vendor>::<event>::<signal>".

Náhledová brána v režimu dry-run

Nasměrujte uložený test na náhledovou URL konkrétního PR a zablokujte nasazení bez poškození produkční baseline. Propojená journey se přehraje v režimu dry-run: vyprodukuje diffy a skóre pro bránu, ale nic nezapíše zpět do journey.

# Gate a per-PR preview deploy WITHOUT mutating the saved journey baseline.
# The target's linked journey is replayed against $PREVIEW_URL as a dry-run:
# it still produces diffs + score for the gate, but writes nothing back to the
# journey's expected schema, vendor watch-list, or regressions.
SID=$(curl -fsS -X POST \
  -H "Authorization: Bearer $AP_KEY" \
  -H 'content-type: application/json' \
  -d "{\"url\":\"$PREVIEW_URL\"}" \
  "$BASE/api/v1/targets/$TARGET_ID/scans" | jq -r .scanId)
# 400 journey_required is returned when the target has no journey to replay.

Kurzorové stránkování

Endpointy se seznamy používají keyset stránkování. Následujte nextCursor, dokud není null; starší příznak truncated je zachován kvůli zpětné kompatibilitě.

# Keyset pagination (ordered createdAt DESC, id DESC):
curl -fsS -H "Authorization: Bearer $AP_KEY" \
  "$BASE/api/v1/scans?limit=20" | jq '{next: .nextCursor, count: (.scans | length)}'

# Pass the previous response's nextCursor to fetch the next page;
# nextCursor is null on the final page (truncated is kept for back-compat):
curl -fsS -H "Authorization: Bearer $AP_KEY" \
  "$BASE/api/v1/scans?limit=20&cursor=$NEXT_CURSOR"

Limity požadavků

Limity se vynucují na úrovni API klíče a jsou uvedeny v každé odpovědi.

  • 60 requests / minute per key. Every response carries X-RateLimit-Limit, X-RateLimit-Remaining, and X-RateLimit-Reset (seconds until the window resets).
  • A 429 response adds Retry-After (seconds).
  • 5 concurrent long-polls per key. Exceeding the cap returns 429 too_many_long_polls with Retry-After: 5.

Idempotence

Endpointy pro spuštění a opětovné spuštění lze bezpečně opakovat s idempotenčním klíčem.

  • Send Idempotency-Key (1–200 chars) as a header, or an idempotencyKey body field — the header wins.
  • An in-flight duplicate is held for 5 minutes; a completed key replays for 24 hours. A replay returns 200 with idempotentReplay: true.
  • A key identifies one logical operation, scoped per project across both trigger endpoints — reusing the same key for a different call replays the original scan's id rather than starting a new scan, so use a fresh key (e.g. the commit SHA) per distinct scan.
# Make retries safe: repeat the same Idempotency-Key header (or "idempotencyKey"
# body field) and a replay returns HTTP 200 with idempotentReplay=true and the
# original scanId instead of starting a second scan.
curl -fsS -X POST \
  -H "Authorization: Bearer $AP_KEY" \
  -H "Idempotency-Key: $GITHUB_SHA" \
  -H 'content-type: application/json' -d '{}' \
  "$BASE/api/v1/targets/$TARGET_ID/scans"

Chybové kódy

Každá chybová odpověď má stabilní tvar: objekt error s poli code, message a volitelným details, plus requestId na nejvyšší úrovni. Kódy selhání brány se objevují v gate.failures[].code, nikoli v error.code.

KódPopis
unauthorizedNo usable credentials were presented.
missing_credentialsNeither an Authorization Bearer token nor an X-API-Key header was sent.
invalid_keyThe API key is not recognised.
key_revokedThe API key has been revoked.
key_expiredThe API key is past its expiry date.
tier_forbiddenThe project tier does not include programmatic API access (Enterprise only).
insufficient_scopeThe key lacks the scope this operation requires.
rate_limitedPer-key request rate limit exceeded — see the Retry-After header.
too_many_long_pollsToo many concurrent long-poll requests for this key (max 5).
invalid_requestThe request body or query failed validation.
not_foundThe referenced scan, target, journey, or upload does not exist for this project.
idempotent_in_progressA prior request with the same Idempotency-Key is still in flight.
not_cancellableThe scan is already terminal and cannot be cancelled.
journey_mismatchThe supplied journeyId does not belong to the target.
journey_requiredA preview-URL gate needs a journey to replay, but the target has none.
no_urlA web/hbbtv scan requires a url.
scan_type_mismatchThe scanType disagrees with the target/upload type.
scan_in_progressThe target already has an active scan.
invalid_targetThe target is not valid for this scanType.
dispatch_failedThe scan could not be enqueued.
internal_errorUnexpected server-side failure — safe to retry.
file_too_largeThe uploaded artifact exceeds the size limit.
package_parse_failedThe APK/XAPK package name could not be parsed.
upload_failedThe artifact could not be stored.
score_below_thresholdgate: compliance score fell below minComplianceScore.
datalayer_regressionsgate: new, unacknowledged dataLayer regressions were found.
severity_exceededgate: a diff met or exceeded failOnSeverity.
status_broken_stepgate: the journey replay broke mid-flow.
status_failedgate: the scan ended in a failed status.
status_cancelledgate: the scan ended cancelled.
new_vendorsgate: a first-seen vendor appeared in the dataLayer.

CI recepty

Spusťte scan, opakovaně se dotazujte, dokud nedosáhne koncového stavu, a poté vyhodnoťte bránu. Při selhání recept vypíše problematické diffy a skončí nenulovým kódem.

Discover the target ID

# Find the TARGET_ID of a saved test (project-scoped):
curl -fsS -H "Authorization: Bearer $AP_KEY" \
  "$BASE/api/v1/targets" | jq -r '.targets[] | "\(.id)\t\(.name)"'

GitHub Actions

# .github/workflows/analyticsproof.yml
- name: AnalyticsProof compliance gate
  env:
    AP_KEY: ${{ secrets.ANALYTICSPROOF_API_KEY }}
    BASE: https://app.analyticsproof.com
    TARGET_ID: <TARGET_ID>   # discover via: GET $BASE/api/v1/targets
  run: |
    set -euo pipefail
    # Trigger a re-scan of the saved target (idempotent per commit SHA)
    SID=$(curl -fsS -X POST \
      -H "Authorization: Bearer $AP_KEY" \
      -H "Idempotency-Key: $GITHUB_SHA" \
      -H 'content-type: application/json' -d '{}' \
      "$BASE/api/v1/targets/$TARGET_ID/scans" | jq -r .scanId)

    GATE=$(jq -rn '{minComplianceScore:90,failOnNewDataLayerRegressions:true,failOnBrokenStep:true} | @uri')

    # Re-poll until the scan reaches a terminal status.
    # ?wait long-polls up to 120s per attempt (server cap 300s).
    RES=''
    for _ in $(seq 1 20); do
      RES=$(curl -fsS -H "Authorization: Bearer $AP_KEY" \
        "$BASE/api/v1/scans/$SID?wait=120&gate=$GATE&include=diffs")
      case "$(echo "$RES" | jq -r .status)" in
        completed|failed|cancelled|broken_step) break ;;
      esac
    done

    echo "$RES" | jq '{status, score, gate, warnings}'
    if [ "$(echo "$RES" | jq -r '.gate.passed')" != "true" ]; then
      echo "::error::AnalyticsProof gate failed"
      echo "$RES" | jq -r '.diffs[]? | "  [\(.severity)] step \(.stepIndex) \(.vendor // "-"): \(.description)"'
      exit 1
    fi

GitLab CI

# .gitlab-ci.yml
analyticsproof:
  stage: verify
  image: alpine:latest
  before_script:
    - apk add --no-cache curl jq
  script:
    - |
      set -euo pipefail
      SID=$(curl -fsS -X POST \
        -H "Authorization: Bearer $ANALYTICSPROOF_API_KEY" \
        -H "Idempotency-Key: $CI_COMMIT_SHA" \
        -H 'content-type: application/json' -d '{}' \
        "https://app.analyticsproof.com/api/v1/targets/$TARGET_ID/scans" | jq -r .scanId)

      GATE=$(jq -rn '{minComplianceScore:90,failOnNewDataLayerRegressions:true,failOnBrokenStep:true} | @uri')

      RES=''
      for _ in $(seq 1 20); do
        RES=$(curl -fsS -H "Authorization: Bearer $ANALYTICSPROOF_API_KEY" \
          "https://app.analyticsproof.com/api/v1/scans/$SID?wait=120&gate=$GATE&include=diffs")
        case "$(echo "$RES" | jq -r .status)" in
          completed|failed|cancelled|broken_step) break ;;
        esac
      done

      echo "$RES" | jq '{status, score, gate, warnings}'
      [ "$(echo "$RES" | jq -r '.gate.passed')" = "true" ] || {
        echo "Compliance gate failed"
        echo "$RES" | jq -r '.diffs[]?'
        exit 1
      }

OpenAPI specifikace

Celé API popisuje strojově čitelný dokument OpenAPI 3.0 — ke čtení není potřeba autentizace.

https://analyticsproof.com/api/v1/openapi.json

URL serveru je absolutní, takže tuto URL můžete vložit přímo do editor.swagger.io nebo Redoc (redocly.com/redoc) a volat API odtud.