Outcome
Run a reviewed manual Consent cookie scan and interpret its external fetches, stored artifacts and limitations. This does not perform a browser compliance assessment.
Action: app_topolo_consent.cookie_scanner.run.
POST /api/projects/{projectKey}/cookie-scanner/run
Before you start
Verify original production identity/organization, canonical Consent workspace read_write-or-higher grant and app_topolo_consent.workspace:write. Discover the intended non-archived project/configured domains using authorized current records. Bind the real workspace separately; project and scan ids are not grants.
This is a confirmed mutation with external HTTPS requests. Approval must cover scanning the actual configured domain set, potential redirects, stored history/settings/index updates and possible completion notification. It does not require enabling the scheduled scanner first, nor does a history read authorize running it.
Use only owned/authorized targets and current reviewed configuration. No real scan/domain/network/notification/model call was performed for this guide; all worked responses are complete synthetic/unrun projections with explicit premises.
Request
{
"projectKey": "project_fixture_docs"
}
Discover the current action, validate the approved input and inspect the exact resource-bound plan. Confirm only the intended real target/effects. No fixture id is authorized for execution by this guide.
Effects and result
The worker requires read_write workspace access and workspace:write; requireAdminProject checks original organization/workspace reachability, control storage and non-archived status. Input is only projectKey; no domain override,limit,cursor,force,mode or scanId is accepted.
Manual run reads that project configuration and does not check settings.enabled. It normalizes configured domains to hostnames, drops unusable entries and selects first25; no deduplication is shown. Each selected hostname is fetched concurrently as HTTPS root GET with redirects followed and a2500ms abort timer. It does not execute browser JavaScript, navigate all pages, log in, manage a cookie jar or simulate consent choices.
A received HTTP response counts as scanned even when response.ok is false; network/abort failures become failed domain records. HTML-body read failures can leave empty script-hint text after headers were received. The status is not a guarantee of HTTP200, a clean site or a complete browser assessment.
Findings combine response Set-Cookie names/attributes with fixed HTML script-token hints. Classification uses source heuristics and configured purpose/vendor mapping; unknown cookie names fall back support/unknown/low confidence. Script hints use medium confidence and null cookie attributes; they do not prove a cookie was set or a provider actually ran.
A generated scan has id,projectKey,status,scannedAt,domains,findings,summary. Status is completed when at least one domain exists and every domain is scanned; otherwise partial only when at least one finding exists, else failed. Consequently some failed domains plus zero findings is failed, not partial, and an empty domain set is failed. Summary includes observed categories only, not every category forced zero.
History append reads existing KV history then writes [newScan,...history].slice(0,20). Next it reads settings and writes lastRunAt=scannedAt, recalculates enabled nextRunAt from completion clock plus daily/weekly interval (disabled stays null), then separately updates scanner index capped500. These nontransactional read-modify-write steps can partially persist or race concurrent scans/settings changes; the scan id is not an idempotency token.
If scanner KV is unbound, history/settings writes return without persistence. The HTTP response may still contain a freshly computed scan but fallback disabled/daily/null settings and empty history. A200 with scan is not durable-history/schedule proof. KV write errors after external fetches can fail the action with partial effects; failure does not mean no scan occurred.
After run returns, the handler attaches notifyScanIssues with scan id/finding count/failed-domain count to ctx.waitUntil only when an execution context is available. Helper eligibility/delivery is separate; do not infer a notification from200, or re-run to force one. Response settings and history are additional independent reads, not an atomic snapshot with scan.
Complete worked responses
Received one page with no classified findings
Complete synthetic/unrun HTTP200 response. Assumes verified original authority, active fixture configuration with one authorized hostname, a received response without recognized findings, previously empty bound KV history and disabled daily schedule. Fixture ids/hostname are illustrative; no fetch or write occurred.
Input:
{
"projectKey": "project_fixture_docs"
}
Response:
{
"scan": {
"id": "cookie_scan_fixture_received",
"projectKey": "project_fixture_docs",
"status": "completed",
"scannedAt": "2026-10-20T08:00:00.000Z",
"domains": [
{
"domain": "site.fixture.invalid",
"status": "scanned",
"findings": []
}
],
"findings": [],
"summary": {}
},
"settings": {
"enabled": false,
"cadence": "daily",
"lastRunAt": "2026-10-20T08:00:00.000Z",
"nextRunAt": null
},
"history": [
{
"id": "cookie_scan_fixture_received",
"projectKey": "project_fixture_docs",
"status": "completed",
"scannedAt": "2026-10-20T08:00:00.000Z",
"domains": [
{
"domain": "site.fixture.invalid",
"status": "scanned",
"findings": []
}
],
"findings": [],
"summary": {}
}
]
}
Completed means the configured fetch returned. It does not certify successful HTTP status, all-site coverage, browser cookies, legal compliance or delivered notification.
No domains and unbound scanner storage
Complete synthetic/unrun HTTP200 source branch. Assumes control storage/project/config access succeeds, configuration has no selected domains and scanner KV is absent. No real binding was removed/probed.
Input:
{
"projectKey": "project_fixture_docs"
}
Response:
{
"scan": {
"id": "cookie_scan_fixture_empty",
"projectKey": "project_fixture_docs",
"status": "failed",
"scannedAt": "2026-10-20T08:00:00.000Z",
"domains": [],
"findings": [],
"summary": {}
},
"settings": {
"enabled": false,
"cadence": "daily",
"lastRunAt": null,
"nextRunAt": null
},
"history": []
}
Treat failed empty scan plus fallback settings/history as no demonstrated site coverage or durable persistence. Do not call it a clean scan or assume a schedule was stored.
Archived project refused before scan
Complete synthetic/unrun HTTP409 branch. Assumes original authority checks succeed and the scoped stored project is archived. This status is external fixture state, not an input flag.
Input:
{
"projectKey": "project_fixture_docs"
}
Response:
{
"error": "project_archived"
}
Preserve the refusal; restoration is a separate approved admin operation. No external fetch should be inferred from this handler branch.
Verify and recover
Compare the actual scan.projectKey/id/scannedAt and each selected domain to intended configuration; inspect failed domains, error labels, finding source/confidence and actual summary. Distinguish completed fetch processing from full browser coverage and downstream provider enforcement.
Inspect returned settings/history separately and, when authorized, use cookie_scanner.history.list to reconcile actual stored scan id. Missing id may reflect missing KV, bounded retention, race or partial persistence; do not claim failure/nonexecution from absence alone. Preserve storage-health uncertainty with the owner.
After an uncertain response, reconcile known scan/time/domain effects and current history/settings first. A repeat creates another scan and external requests and may change schedule/history again. Do not blindly replay or run parallel duplicates. Notification delivery requires its own actual receipt/owner evidence.
External GET requests and any delivered notification cannot be undone. This action has no targeted scan deletion or exact rollback input. Preserve actual outcome evidence and reconcile partial history/settings/index before any separately authorized repeat or schedule adjustment. Do not delete history, clear KV or re-run as rollback, and do not overwrite concurrent owner changes.
Refusals
403 / organization_context_required: Reconcile original intended context; do not broaden principal.
404 / project_not_found: Reconcile actual key in original organization and verified workspace.
409 / project_archived: Do not restore or substitute a project merely to bypass the refusal.
503 / database_unavailable: Control storage required to resolve project is absent; preserve actual error. Scanner KV is different and can follow no-persistence fallback.
Outer Auth/workspace/permission and shared-runtime storage-error carriers require actual request evidence. Unsupported domain/force/limit fields are local strict preflight failures, not captured HTTP responses. No live scan or notification was captured here.
Change log
- 2026-10-21: Added manual versus scheduled eligibility, exact25-domain/root-fetch and status semantics, heuristic limitations, nontransactional history/settings/index effects, missing-KV persistence ambiguity and three complete illustrative unrun responses.