API v1.2

Vývojářské API

REST API pro CI: spouštějte skeny, 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, a to od tarifu Starter. Posílejte jej jako Bearer token nebo hlavičku X-API-Key. Každý klíč má jedno oprávnění (read, edit nebo admin); celý klíč se zobrazí pouze jednou při vytvoření. Když projekt přejde na Free, klíče zůstanou uložené, ale odpovídají plan_required, dokud se tarif znovu nezvýší.

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

Oprávnění

Oprávnění
OprávněníPopis
read List targets, journeys, schedules and scan history; read a journey definition, monitored vendors and a scan’s full evidence; poll a scan, evaluate a CI gate and explain a result.
edit Everything Read can do, plus trigger, re-run and cancel scans, upload APK/XAPK files, acknowledge dataLayer diffs and change monitored vendors.
admin Everything Edit can do, plus manage the project’s API keys. An admin key cannot mint another admin key.

Endpointy

Všechny endpointy vycházejí ze základní URL API a vyžadují uvedené oprávnění. Dva veřejné endpointy skenu klíč nepotřebují.

Endpointy
MetodaEndpointOprávněníShrnutí
GET /api/v1/scans read List scan history
POST /api/v1/scans edit Trigger an ad-hoc scan
GET /api/v1/scans/{id} read Poll a scan + evaluate a CI gate
DELETE /api/v1/scans/{id} edit Cancel a pending/running scan
POST /api/v1/scans/{id}/explain read Explain a scan in plain language
GET /api/v1/scans/{id}/evidence read Download all evidence of a scan
GET /api/v1/targets read List saved tests
POST /api/v1/targets/{id}/scans edit Re-run a saved test, "Scan again"
GET /api/v1/journeys read List saved journeys
GET /api/v1/journeys/{id} read Get a saved journey with its steps
GET /api/v1/schedules read List scan schedules
POST /api/v1/uploads edit Upload a CI APK/XAPK
POST /api/v1/journeys/{id}/datalayer/approve edit Approve a dataLayer diff into the journey baseline
POST /api/v1/journeys/{id}/datalayer/approve-all edit Approve all pending dataLayer diffs for a scan
POST /api/v1/journeys/{id}/datalayer/ignore edit Ignore a dataLayer element journey-wide
DELETE /api/v1/journeys/{id}/datalayer/ignores/{decisionId} edit Remove a standing dataLayer ignore
GET /api/v1/journeys/{id}/datalayer/decisions read List standing dataLayer ignore decisions
GET /api/v1/targets/{id}/vendors read List a target's vendors and their monitoring state
PUT /api/v1/targets/{id}/vendors edit Set vendor-monitoring states across a target's journeys
GET /api/v1/whoami read Identify the calling key
GET /api/v1/keys admin List project API keys
POST /api/v1/keys admin Mint a project API key
DELETE /api/v1/keys/{keyId} admin Revoke a project API key
POST /api/v1/public/scans — Request a free public Web Compliance scan (public, no API key)
GET /api/v1/public/scans/{slug} — Read a public scan by slug (public, no API key)

Možnosti CI brány

Při dotazování na sken 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žnosti CI brány
MožnostTypPopis
minComplianceScore number (0–100) Fail when the compliance score is below this threshold (score_below_threshold).
failOnNewDataLayerRegressions boolean Fail on new, unacknowledged dataLayer regressions (datalayer_regressions) in watched vendors.
failOnSeverity "info" | "warning" | "critical" Fail when any diff meets or exceeds this severity (severity_exceeded).
failOnBrokenStep boolean Fail when the journey replay broke mid-flow (status_broken_step).
failOnStatus "failed" | "cancelled" | "broken_step" | "no_data"[] Fail when the scan ends in any of the listed statuses (status_<status>). Note: status_no_data always fails, whether or not it is listed.
failOnNewVendors boolean Fail when a vendor appears for the first time that the journey watches by default (dataLayer, adobeDataLayer, Google Analytics 4, Adobe Analytics; on mobile app targets also the app's own tag container (Google Tag Manager)) on a journey that already has a vendor list (new_vendors).
Upozornění
Neblokující upozornění (například brána dataLayer požadovaná pro sken 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 sken, 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.

http
# 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 | consent_unreadable |
# step_not_reached. elementKey is "event:<vendor>::<name>",
# "param:<vendor>::<event>::<param>", "consent:<vendor>::<event>::<country>.<signal>",
# or "step:<index>" for step_not_reached, which belongs to no vendor.

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. Se scanType web_journey se jedna cesta (journeyId, jinak první spustitelná) přehraje v režimu dry-run: vyprodukuje diffy pro bránu, ale nic nezapíše zpět do cesty. Bez scanType spustí test webu svůj výchozí sken, tedy compliance audit. S journeys: all se přehrají všechny spustitelné cesty a odpověď místo jednoho scanId obsahuje seznam scans. Náhledové běhy vyžadují tarif Professional; v nižších tarifech odpoví 403 plan_required.

bash
# Gate a per-PR preview deploy WITHOUT mutating the saved journey baseline.
# "scanType":"web_journey" replays one journey against $PREVIEW_URL as a
# dry-run: it produces diffs for the gate, but writes nothing back to the
# journey's expected schema, vendor watch-list, or regressions. Without a
# scanType a website test runs its default scan, a compliance audit.
# Add "journeyId" to pick the journey; omitted, the first runnable one is used.
#
# Idempotency-Key: commit + CI run ($RUN), mode, target and a HASH of the preview
# URL (plus any journeyId / maxJourneys you send). A retry of the same request
# replays its scan; a new push gets a fresh one (a finished key replays for 24 h),
# and the hash keeps the key within 200 chars. RUN below is for GitHub Actions;
# on another CI use its commit SHA and job id (GitLab: $CI_COMMIT_SHA-$CI_JOB_ID).
RUN="${GITHUB_SHA}-${GITHUB_RUN_ID}-${GITHUB_RUN_ATTEMPT:-1}"
URL_HASH=$(printf '%s' "$PREVIEW_URL" | cksum | cut -d' ' -f1)
SID=$(curl -fsS -X POST \
  -H "Authorization: Bearer $AP_KEY" \
  -H "Idempotency-Key: $RUN-web_journey-$TARGET_ID-$URL_HASH" \
  -H 'content-type: application/json' \
  -d "$(jq -cn --arg url "$PREVIEW_URL" '{url: $url, scanType: "web_journey"}')" \
  "$BASE/api/v1/targets/$TARGET_ID/scans" | jq -r .scanId)
# 400 journey_required is returned when the target has no journey to replay.

# Every runnable journey at once: "journeys":"all" (optional "maxJourneys").
# The response has NO top-level scanId, only scans[] (scanId + pollUrl per
# journey) and failed[] (journeys that could not be started).
RES=$(curl -fsS -X POST \
  -H "Authorization: Bearer $AP_KEY" \
  -H "Idempotency-Key: $RUN-all-$TARGET_ID-$URL_HASH" \
  -H 'content-type: application/json' \
  -d "$(jq -cn --arg url "$PREVIEW_URL" '{url: $url, journeys: "all"}')" \
  "$BASE/api/v1/targets/$TARGET_ID/scans")
[ "$(echo "$RES" | jq '.failed | length')" = "0" ] || { echo "$RES" | jq .failed; exit 1; }
# Then poll each "$BASE$POLL" in turn (not in parallel: five waits per key) with
# the single-scan loop from the CI recipes, and fail the job unless every gate passed.
for POLL in $(echo "$RES" | jq -r '.scans[].pollUrl'); do echo "$BASE$POLL"; done

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ě.

bash
# 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.
  • While the first request is still being processed, a duplicate gets 409 idempotent_in_progress with Retry-After: 5 — it is retryable. The in-flight hold is 5 minutes on POST /api/v1/scans and 60 seconds on POST /api/v1/targets/{id}/scans.
  • A completed key replays for 24 hours. A preview scan on a test (a request with url) is tied to the key and the request body: the same key with the same body returns the same scans for as long as they exist, also after 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.
bash
# 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.

Chybové kódy
KódPopis
unauthorized No usable credentials were presented.
missing_credentials Neither an Authorization Bearer token nor an X-API-Key header was sent.
invalid_key The API key is not recognised.
key_revoked The API key has been revoked.
key_expired The API key is past its expiry date.
plan_required The project's plan does not include this operation. error.details.minTier names the lowest plan that does: starter for any keyed API call, professional for Android/TV scans, APK uploads and preview runs (dryRun, a journey replayed against another url, journeys:"all").
insufficient_permission The key’s permission is below the one this operation requires (read < edit < admin).
rate_limited Per-key request rate limit exceeded — see the Retry-After header.
too_many_long_polls Too many concurrent long-poll requests for this key (max 5).
invalid_request The request body or query failed validation.
not_found The referenced scan, target, journey, or upload does not exist for this project.
idempotent_in_progress A prior request with the same Idempotency-Key is still in flight. Retryable: wait for Retry-After (5 s) and send the same request again.
idempotency_conflict The same Idempotency-Key was reused with a different payload on the same resource (e.g. single-journey vs journeys:"all"). Use a new key when the payload changes.
idempotency_corrupt The stored idempotency record for this key could not be read. Retry with a new Idempotency-Key.
not_cancellable The scan is already terminal and cannot be cancelled.
scan_not_terminal The scan has not reached a terminal status yet, so the requested artifact (SARIF / explain) is not available. Poll GET /scans/{id} first.
evidence_too_large The evidence document is larger than 25 MB. Request fewer steps with ?steps= (error.details.stepCount says how many were selected).
journey_mismatch The supplied journeyId does not belong to the target.
journey_required A preview-URL gate needs a journey to replay, but the target has none.
journey_sleeping The journeyId is ranked beyond the plan's per-target journey quota. Reorder the target's journeys or upgrade the plan.
journey_over_step_cap The journeyId has more steps than the plan's per-journey step limit. Shorten the journey (re-record it) or upgrade the plan.
no_url A Web or TV scan requires a url.
scan_type_mismatch The scanType disagrees with the target/upload type.
scan_in_progress The target already has an active scan (public scans: the same URL is already queued or running; poll the returned statusUrl).
target_quota_exceeded persist: true would save a second test where the project already has one of the same family on that target type — one saved test per family, as in the dashboard (Web Compliance and Web Journey share the one website test; likewise TV and Android).
invalid_target The target is not valid for this scanType.
dispatch_failed The scan could not be enqueued.
public_scans_disabled Public scans are temporarily switched off by the operator. Retry later; keyed scans are unaffected.
target_has_no_journeys A target-scoped vendor-monitoring write had no journeys to fan out to. Record a journey on the target first.
open_regression Approving this missing event or param would remove an element that is marked as an error at the same step. Resolve that error first.
internal_error Unexpected server-side failure — safe to retry.
file_too_large The uploaded artifact exceeds the size limit.
package_parse_failed The APK/XAPK package name could not be parsed.
upload_failed The artifact could not be stored.
score_below_threshold gate: compliance score fell below minComplianceScore.
datalayer_regressions gate: new, unacknowledged dataLayer regressions were found.
severity_exceeded gate: a diff met or exceeded failOnSeverity.
status_broken_step gate/warning: the journey replay broke mid-flow. It fails only when explicitly configured as a generic API gate predicate; the fixed GitHub policy reports it as a non-blocking warning.
status_failed gate: the scan ended in a failed status.
status_cancelled gate: the scan ended cancelled.
status_no_data gate: the scan produced no trustworthy measurement, so it has no score and cannot be gated on one. Always fails, whether or not it is listed in failOnStatus.
new_vendors gate: a first-seen vendor appeared in the dataLayer.
diffs_not_persisted gate: the scan finished but its findings could not be stored (including a diff-engine error), so the diff list is incomplete. Always fails — an empty diff list here means "not written", not "nothing found". An automatic replay within 6 hours of the scan clears it when it succeeds; otherwise re-run the scan.
verdict_scope_partial gate: the consent banner could not be shown or offers no accept and no reject control, so the compliance score covers only how the page behaves before consent — the accept and reject flows were not tested. Always fails, whatever the score; score_below_threshold is still reported beside it when the score is also low.
no_tracking_observed gate: the page loaded but no tracking tool and no consent banner were observed on it, so it has no compliance score. Always fails — a preview that lost its tracking looks exactly like this.
infrastructure_interrupted gate: the scan was interrupted on the AnalyticsProof side (for example a restart, a shutdown or our own database failing), so the result says nothing about the page. Reported instead of status_failed, with no score_below_threshold beside it; always fails (the API stays fail-closed) — re-run the scan. failureOrigin on such a scan is normally "infrastructure", but failureOrigin is broader: a scanner-side failure can carry it and still fail as status_failed.

CI recepty

Spusťte sken, opakovaně se dotazujte, dokud nedosáhne koncového stavu a settled už není false, a poté vyhodnoťte bránu. Koncová odpověď se settled: false je předběžná — nálezy se ještě ukládají. Při selhání recept vypíše problematické diffy a skončí nenulovým kódem. Hlídání náhledového nasazení, jak ho dělají tyto recepty, vyžaduje tarif Professional; spuštění uloženého testu Web Journey a dotazování na výsledek funguje od tarifu Starter, testy Web Compliance, Android a TV spustíte na vyžádání od tarifu Professional.

Celý postup: CI/CD přes API

GitHub Action (více cest)

Kompozitní action zabalí celou smyčku spuštění, dotazování, brány, SARIF a komentáře do jediného bloku uses:. Proti náhledové URL přehraje všechny spustitelné cesty připuštěné kvótou (koncepty a rozbité cesty zahrne, pozastavené vynechá), hlásí pouze přímé změny dataLayer a blokuje jen kritické nálezy. Varování, informace a nedokončené přehrání cesty zůstávají neblokující zpětnou vazbou; chyby spuštění a scanneru nadále bezpečně selžou. Vyžaduje tajný klíč repozitáře ANALYTICSPROOF_API_KEY a oprávnění contents: read, pull-requests: write, security-events: write.

yaml
# .github/workflows/analyticsproof.yml
name: AnalyticsProof
on: pull_request
concurrency:
  group: analyticsproof-${{ github.ref }}
  cancel-in-progress: true
permissions:
  contents: read           # checkout
  pull-requests: write     # PR comment
  security-events: write   # SARIF upload
jobs:
  gate:
    runs-on: ubuntu-latest
    if: github.event.pull_request.head.repo.full_name == github.repository
    timeout-minutes: 20   # poll deadline (15 min default) + at least 5 min (start retries, SARIF, comment)
    steps:
      - uses: actions/checkout@v4
      - uses: your-org/your-repo/.github/actions/analyticsproof@v1
        with:
          api-key: ${{ secrets.ANALYTICSPROOF_API_KEY }}
          target-id: <TARGET_ID>   # discover via: GET /api/v1/targets
          url: https://preview-${{ github.event.number }}.example.com
          all-journeys: 'true'     # run EVERY runnable, quota-admitted journey
      - if: always() && hashFiles('analyticsproof.sarif') != ''
        uses: github/codeql-action/upload-sarif@v4
        with:
          sarif_file: analyticsproof.sarif
          category: analyticsproof

Zjištění ID testu

bash
# 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

yaml
# .github/workflows/analyticsproof.yml — gate one journey of a PR preview (dry run)
# Run it only on pull requests (on: pull_request), so the preview belongs to the PR.
- name: AnalyticsProof dataLayer gate
  env:
    AP_KEY: ${{ secrets.ANALYTICSPROOF_API_KEY }}
    BASE: https://analyticsproof.com
    TARGET_ID: <TARGET_ID>   # discover via: GET $BASE/api/v1/targets
    PREVIEW_URL: https://preview-${{ github.event.number }}.example.com
  run: |
    set -euo pipefail
    # scanType web_journey replays the first runnable journey (add "journeyId"
    # to pick one); without it a website test runs its compliance audit.
    # Key = commit + run id + run attempt + mode + target + preview-URL hash (add any
    # journeyId you send): a retry replays its scan, a new push, workflow run or
    # re-run gets a fresh one, and it stays under 200 chars.
    URL_HASH=$(printf '%s' "$PREVIEW_URL" | cksum | cut -d' ' -f1)
    SID=$(curl -fsS -X POST \
      -H "Authorization: Bearer $AP_KEY" \
      -H "Idempotency-Key: $GITHUB_SHA-$GITHUB_RUN_ID-${GITHUB_RUN_ATTEMPT:-1}-web_journey-$TARGET_ID-$URL_HASH" \
      -H 'content-type: application/json' \
      -d "$(jq -cn --arg url "$PREVIEW_URL" '{url: $url, scanType: "web_journey"}')" \
      "$BASE/api/v1/targets/$TARGET_ID/scans" | jq -r .scanId)

    # A journey is judged by its diffs; minComplianceScore belongs to a compliance test.
    GATE=$(jq -rn '{failOnSeverity:"critical",failOnBrokenStep:true} | @uri')

    # Re-poll until the scan reaches a terminal status and its findings have
    # settled (a terminal answer with settled:false is provisional).
    # ?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&scope=direct-datalayer")
      case "$(echo "$RES" | jq -r 'if .settled == false then "settling" else .status end')" in
        completed|failed|cancelled|broken_step|no_data) break ;;
      esac
    done

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

GitLab CI

yaml
# .gitlab-ci.yml — gate one journey of a merge-request preview (dry run)
analyticsproof:
  stage: verify
  image: alpine:latest
  variables:
    BASE: https://analyticsproof.com
    TARGET_ID: <TARGET_ID>   # discover via: GET $BASE/api/v1/targets
    PREVIEW_URL: https://preview-$CI_MERGE_REQUEST_IID.example.com
  rules:
    - if: $CI_PIPELINE_SOURCE == "merge_request_event"
  before_script:
    - apk add --no-cache curl jq
  script:
    - |
      set -euo pipefail
      # scanType web_journey replays the first runnable journey (add "journeyId"
      # to pick one); without it a website test runs its compliance audit.
      # Key = commit + job + mode + target + preview-URL hash (add any journeyId you
      # send): a retry of the same request replays its scan, a new push or a job
      # retry gets a fresh one, and it stays under 200 chars.
      URL_HASH=$(printf '%s' "$PREVIEW_URL" | cksum | cut -d' ' -f1)
      SID=$(curl -fsS -X POST \
        -H "Authorization: Bearer $ANALYTICSPROOF_API_KEY" \
        -H "Idempotency-Key: $CI_COMMIT_SHA-$CI_JOB_ID-web_journey-$TARGET_ID-$URL_HASH" \
        -H 'content-type: application/json' \
        -d "$(jq -cn --arg url "$PREVIEW_URL" '{url: $url, scanType: "web_journey"}')" \
        "$BASE/api/v1/targets/$TARGET_ID/scans" | jq -r .scanId)

      # A journey is judged by its diffs; minComplianceScore belongs to a compliance test.
      GATE=$(jq -rn '{failOnSeverity:"critical",failOnBrokenStep:true} | @uri')

      RES=''
      for _ in $(seq 1 20); do
        RES=$(curl -fsS -H "Authorization: Bearer $ANALYTICSPROOF_API_KEY" \
          "$BASE/api/v1/scans/$SID?wait=120&gate=$GATE&include=diffs&scope=direct-datalayer")
        case "$(echo "$RES" | jq -r 'if .settled == false then "settling" else .status end')" in
          completed|failed|cancelled|broken_step|no_data) break ;;
        esac
      done

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

Změny API

Od 2026-10-10: každá cesta ve výchozím stavu sleduje dataLayer, adobeDataLayer, Google Analytics 4, Adobe Analytics (v mobilní aplikaci i její vlastní kontejner Google Tag Manager), každý jen pokud ho web používá. Jakýkoli nástroj lze přes endpoint vendors zapnout i vypnout; 422 vendor_not_alert_ready se už nevrací. Approve a approve-all přijímají volitelný mode (výchozí required, nebo optional). scope=direct-datalayer na seznamu sledovaných nástrojů nezávisí.

Pro AI agenty

Agent si může bez klíče vyžádat bezplatný sken Web Compliance veřejné URL: POST /api/v1/public/scans vrátí slug, na který se pak dotazuje, nebo výsledek z mezipaměti, pokud byla URL skenována za posledních 28 dní (parametry dotazu a fragment se ignorují). S klíčem s oprávněním read vrátí GET /api/v1/scans/<id skenu>/evidence vše, co je o jednom skenu uložené, včetně odkazů na snímky obrazovky platných 15 minut.

bash
# No key: request the free Web Compliance scan of one URL.
# 202 with a slug to poll, or 200 with cached: true and the result when the
# URL (query string and fragment ignored) was scanned in the last 28 days.
curl -fsS -X POST "$BASE/api/v1/public/scans" \
  -H "Content-Type: application/json" -d '{"url":"https://www.example.com"}'
curl -fsS "$BASE/api/v1/public/scans/<SLUG>"

# With a key of permission read: everything stored about one scan.
# Screenshot links in it work without a session for 15 minutes.
curl -fsS -H "Authorization: Bearer $AP_KEY" \
  "$BASE/api/v1/scans/<SCAN_ID>/evidence?steps=0,3"

OpenAPI specifikace

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

url
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.