# EU Withdrawal Button Compliance Auditor (`trovevault/eu-withdrawal-button-auditor`) Actor

Audits websites for observable EU withdrawal-button risk signals, including withdrawal function visibility, confirmation flow, acknowledgement signals, friction, findings, and evidence.

- **URL**: https://apify.com/trovevault/eu-withdrawal-button-auditor.md
- **Developed by:** [Trove Vault](https://apify.com/trovevault) (community)
- **Categories:** E-commerce, News, Integrations
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 1 bookmarks
- **User rating**: No ratings yet

## Pricing

from $5.10 / 1,000 stores

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.
Since this Actor supports Apify Store discounts, the price gets lower the higher subscription plan you have.

Learn more: https://docs.apify.com/platform/actors/running/actors-in-store#pay-per-event

## What's an Apify Actor?

Actors are a software tools running on the Apify platform, for all kinds of web data extraction and automation use cases.
In Batch mode, an Actor accepts a well-defined JSON input, performs an action which can take anything from a few seconds to a few hours,
and optionally produces a well-defined JSON output, datasets with results, or files in key-value store.
In Standby mode, an Actor provides a web server which can be used as a website, API, or an MCP server.
Actors are written with capital "A".

## How to integrate an Actor?

If asked about integration, you help developers integrate Actors into their projects.
You adapt to their stack and deliver integrations that are safe, well-documented, and production-ready.
The best way to integrate Actors is as follows.

In JavaScript/TypeScript projects, use official [JavaScript/TypeScript client](https://docs.apify.com/api/client/js.md):

```bash
npm install apify-client
```

In Python projects, use official [Python client library](https://docs.apify.com/api/client/python.md):

```bash
pip install apify-client
```

In shell scripts, use [Apify CLI](https://docs.apify.com/cli/docs.md):

````bash
# MacOS / Linux
curl -fsSL https://apify.com/install-cli.sh | bash
# Windows
irm https://apify.com/install-cli.ps1 | iex
```bash

In AI frameworks, you might use the [Apify MCP server](https://docs.apify.com/platform/integrations/mcp.md).

If your project is in a different language, use the [REST API](https://docs.apify.com/api/v2.md).

For usage examples, see the [API](#api) section below.

For more details, see Apify documentation as [Markdown index](https://docs.apify.com/llms.txt) and [Markdown full-text](https://docs.apify.com/llms-full.txt).


# README

## EU Withdrawal Button Compliance Auditor

EU Withdrawal Button Compliance Auditor checks public websites for observable risk signals related to the EU online withdrawal-button requirement. Give it one or more URLs and it opens each site in a real browser, follows likely withdrawal, cancellation, account, subscription, legal, refund, and terms pages, then returns a structured risk audit with findings, evidence, recommended actions, and a screenshot key.

This actor does not certify legal compliance and does not replace legal advice. It is built for fast triage: finding visible issues that compliance teams, consumer-law specialists, agencies, SaaS operators, marketplaces, and e-commerce teams should review before the June 2026 enforcement deadline and after future site or checkout changes.

### Legal Context

The audit is shaped around public, browser-observable signals associated with the EU withdrawal-function requirement introduced through Directive (EU) 2023/2673. Always review the applicable national implementation, business model, contract type, and logged-in customer journey before making a legal conclusion.

### Why Use This Actor

- Check whether a public website exposes a visible online withdrawal or cancellation function.
- Detect whether withdrawal rights are mentioned in terms but not reflected in a prominent online control.
- Find account, subscription, cancellation, terms, refund, return, and withdrawal pages from a submitted homepage.
- Flag processes that appear to rely on email, postal mail, phone, paper forms, support tickets, signatures, or manual review.
- Look for confirmation-step and acknowledgement signals such as "confirm withdrawal", confirmation email, ticket, reference, or receipt language.
- Produce a risk score, risk level, findings, recommended actions, compact evidence samples, and screenshot evidence.
- Schedule recurring audits for owned sites, client portfolios, acquisition targets, subscription flows, or high-risk consumer journeys.

### What It Checks

For each submitted website, the actor performs a standard public audit by default. There are no per-check toggles because the product is the complete withdrawal-button triage pass.

The actor checks:

- visible withdrawal, cancellation, unsubscribe, terminate, account, and subscription controls
- exact or near-exact withdrawal-button wording such as "withdraw from contract here" and "confirm withdrawal"
- public terms, legal, return, refund, cancellation, and withdrawal-rights language
- 14-day, cooling-off, and withdrawal-period language
- whether withdrawal language appears with online, account, form, portal, digital, or button language
- whether the withdrawal or cancellation path appears prominent and above the fold
- confirmation-step signals and final confirmation button wording
- acknowledgement or receipt signals after request submission, where public text reveals them
- email-only, postal-only, phone-only, paper, signature, fax, support-ticket, or manual-review friction
- likely login-gated account or subscription paths that need manual or authenticated follow-up
- homepage screenshot evidence stored in the key-value store

### Use Cases

Compliance teams can audit owned domains before the EU withdrawal-button deadline, then rerun after checkout, account, or subscription-management releases.

Consumer-law and privacy consultants can run first-pass checks across client portfolios before deeper manual review.

Agencies can monitor whether new campaign sites, storefronts, membership pages, or subscription funnels still expose the required cancellation and withdrawal path.

E-commerce and SaaS operators can find mismatches between legal terms and the actual public user interface.

M&A and vendor-risk teams can triage consumer-facing websites before acquisition, onboarding, or commercial review.

### Input Example

```json
{
  "startUrls": [
    { "url": "https://example.com/" },
    { "url": "https://example.org/terms" }
  ],
  "proxyConfiguration": {
    "useApifyProxy": false
  }
}
````

### Input Reference

| Field | Type | Description |
|---|---|---|
| `startUrls` | array | Public website URLs to audit. Use homepages for full-site triage or specific checkout, account, subscription, terms, cancellation, or withdrawal pages when you already know the relevant flow. |
| `proxyConfiguration` | object | Optional Apify Proxy settings. Enable it when sites block datacenter traffic or show location-specific EU behavior. |
| `datasetId` | string | Optional existing dataset to append results to. |
| `runId` | string | Optional upstream job or workflow ID copied into output rows. |

### Output Example

```json
{
  "url": "https://example.com/",
  "finalUrl": "https://example.com/",
  "domain": "example.com",
  "auditStatus": "success",
  "withdrawalRiskScore": 78,
  "riskLevel": "critical",
  "withdrawalFunctionDetected": false,
  "withdrawalButtonProminent": false,
  "withdrawalButtonLabel": null,
  "withdrawalPageUrl": null,
  "accountOrCancellationPageFound": true,
  "termsWithdrawalLanguageFound": true,
  "onlineWithdrawalLanguageFound": false,
  "withdrawalDeadlineLanguageFound": true,
  "confirmationStepDetected": false,
  "confirmWithdrawalButtonDetected": false,
  "confirmationButtonTextDetected": null,
  "acknowledgementMechanismDetected": false,
  "emailOnlyWithdrawalDetected": true,
  "loginRequiredLikely": true,
  "cancellationFrictionSignals": ["postal_or_letter_required", "support_or_manual_review"],
  "pagesChecked": [
    "https://example.com/",
    "https://example.com/terms",
    "https://example.com/account"
  ],
  "findings": [
    {
      "severity": "high",
      "category": "button-prominence",
      "message": "Withdrawal rights are mentioned, but no prominent withdrawal button or equivalent visible control was found.",
      "evidence": ["Consumers may withdraw within 14 days by contacting customer service."]
    }
  ],
  "recommendedActions": [
    "Make the withdrawal button prominent and label it with unambiguous withdrawal language.",
    "Run a logged-in manual review or authenticated audit for account-only subscription and cancellation pages."
  ],
  "screenshotKey": "SCREENSHOT-example.com-1780000000000.png",
  "auditedAt": "2026-06-19T12:00:00.000Z"
}
```

### Output Fields

The main decision fields are `withdrawalRiskScore`, `riskLevel`, `withdrawalFunctionDetected`, `withdrawalButtonProminent`, `withdrawalButtonLabel`, `withdrawalPageUrl`, `termsWithdrawalLanguageFound`, `onlineWithdrawalLanguageFound`, `confirmationStepDetected`, `confirmWithdrawalButtonDetected`, `acknowledgementMechanismDetected`, `emailOnlyWithdrawalDetected`, `loginRequiredLikely`, `cancellationFrictionSignals`, `findings`, `recommendedActions`, `evidence`, and `screenshotKey`.

### API Usage

```bash
curl "https://api.apify.com/v2/acts/TroveVault~eu-withdrawal-button-auditor/runs" \
  -X POST \
  -H "Authorization: Bearer $APIFY_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "startUrls": [
      { "url": "https://example.com/" }
    ]
  }'
```

### Interpreting Results

The actor reports automated risk signals, not legal conclusions. A `high` or `critical` result means the website deserves human review. A `low` result means this automated pass did not find strong visible issues, not that the site is legally compliant. If the real journey appears only after login, purchase, native-app access, or a customer-specific account area, the actor can flag that follow-up is likely needed but cannot complete that private workflow.

### Limitations

- The actor cannot certify compliance with EU consumer law or any national implementation.
- It does not make purchases, create accounts, log in, cancel real subscriptions, or submit withdrawal requests.
- It cannot verify private account flows, backend acknowledgements, email delivery, customer-service SLAs, or contract-specific eligibility.
- Some sites hide cancellation or withdrawal flows behind authentication, geolocation, anti-bot controls, app-only experiences, or customer-specific portals.
- Text detection is multilingual but pattern-based. Unusual wording, images-only buttons, custom web components, or shadow DOM may reduce accuracy.
- Region-specific behavior may require proxy settings that match the target EU market.

### FAQ

#### Does this actor give legal advice?

No. It produces structured observable signals for triage. A qualified legal or compliance reviewer should decide whether the site satisfies the applicable requirement.

#### Can I monitor the same sites every week?

Yes. Use Apify schedules and optionally provide `datasetId` and `runId` so results from recurring runs can be joined in your own pipeline.

### Related Actors

For broader privacy, cookie-consent, tracker, policy, and form-risk triage, use TroveVault's GDPR Website Compliance Auditor. This withdrawal-button actor is narrower and focuses on the consumer withdrawal/cancellation path.

### Changelog

- `0.1`: Initial TroveVault release candidate with browser-based public withdrawal-button risk auditing, legal/cancellation page discovery, confirmation and acknowledgement checks, friction detection, findings, recommendations, and screenshot evidence.

### Support

Open an Apify issue or contact TroveVault with the run ID, target URL, and a short description of the result you expected.

# Actor input Schema

## `startUrls` (type: `array`):

One or more public website URLs to audit. Use homepages for full-site triage, or specific checkout, account, subscription, terms, cancellation, or withdrawal pages when you already know the relevant flow.

## `proxyConfiguration` (type: `object`):

Proxy settings for browser visits. Leave disabled for open websites. Enable Apify Proxy when a target blocks datacenter traffic, shows regional EU behavior only from specific locations, or returns 403, 429, CAPTCHA, or bot-protection pages.

## `datasetId` (type: `string`):

ID of an existing Apify dataset to append audit results to, in addition to the default run dataset. Use this for recurring compliance pipelines that combine multiple scheduled runs into one dataset. Leave blank to write only to the default run dataset.

## `runId` (type: `string`):

ID of an upstream audit, client job, or scheduled workflow to associate with results. The actor copies this value into output rows so external systems can connect withdrawal-button findings to their own pipeline. Leave blank for standalone runs.

## Actor input object example

```json
{
  "startUrls": [
    {
      "url": "https://example.com/",
      "label": "Example website"
    }
  ],
  "proxyConfiguration": {
    "useApifyProxy": false
  }
}
```

# Actor output Schema

## `dataset` (type: `string`):

No description

# API

You can run this Actor programmatically using our API. Below are code examples in JavaScript, Python, and CLI, as well as the OpenAPI specification and MCP server setup.

## JavaScript example

```javascript
import { ApifyClient } from 'apify-client';

// Initialize the ApifyClient with your Apify API token
// Replace the '<YOUR_API_TOKEN>' with your token
const client = new ApifyClient({
    token: '<YOUR_API_TOKEN>',
});

// Prepare Actor input
const input = {
    "startUrls": [
        {
            "url": "https://example.com/",
            "label": "Example website"
        }
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("trovevault/eu-withdrawal-button-auditor").call(input);

// Fetch and print Actor results from the run's dataset (if any)
console.log('Results from dataset');
console.log(`💾 Check your data here: https://console.apify.com/storage/datasets/${run.defaultDatasetId}`);
const { items } = await client.dataset(run.defaultDatasetId).listItems();
items.forEach((item) => {
    console.dir(item);
});

// 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/js/docs

```

## Python example

```python
from apify_client import ApifyClient

# Initialize the ApifyClient with your Apify API token
# Replace '<YOUR_API_TOKEN>' with your token.
client = ApifyClient("<YOUR_API_TOKEN>")

# Prepare the Actor input
run_input = { "startUrls": [{
            "url": "https://example.com/",
            "label": "Example website",
        }] }

# Run the Actor and wait for it to finish
run = client.actor("trovevault/eu-withdrawal-button-auditor").call(run_input=run_input)

# Fetch and print Actor results from the run's dataset (if there are any)
print("💾 Check your data here: https://console.apify.com/storage/datasets/" + run["defaultDatasetId"])
for item in client.dataset(run["defaultDatasetId"]).iterate_items():
    print(item)

# 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/python/docs/quick-start

```

## CLI example

```bash
echo '{
  "startUrls": [
    {
      "url": "https://example.com/",
      "label": "Example website"
    }
  ]
}' |
apify call trovevault/eu-withdrawal-button-auditor --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "command": "npx",
            "args": [
                "mcp-remote",
                "https://mcp.apify.com/?tools=trovevault/eu-withdrawal-button-auditor",
                "--header",
                "Authorization: Bearer <YOUR_API_TOKEN>"
            ]
        }
    }
}

```

## OpenAPI specification

```json
{
    "openapi": "3.0.1",
    "info": {
        "title": "EU Withdrawal Button Compliance Auditor",
        "description": "Audits websites for observable EU withdrawal-button risk signals, including withdrawal function visibility, confirmation flow, acknowledgement signals, friction, findings, and evidence.",
        "version": "0.1",
        "x-build-id": "MkORCNn9gcIxSogO3"
    },
    "servers": [
        {
            "url": "https://api.apify.com/v2"
        }
    ],
    "paths": {
        "/acts/trovevault~eu-withdrawal-button-auditor/run-sync-get-dataset-items": {
            "post": {
                "operationId": "run-sync-get-dataset-items-trovevault-eu-withdrawal-button-auditor",
                "x-openai-isConsequential": false,
                "summary": "Executes an Actor, waits for its completion, and returns Actor's dataset items in response.",
                "tags": [
                    "Run Actor"
                ],
                "requestBody": {
                    "required": true,
                    "content": {
                        "application/json": {
                            "schema": {
                                "$ref": "#/components/schemas/inputSchema"
                            }
                        }
                    }
                },
                "parameters": [
                    {
                        "name": "token",
                        "in": "query",
                        "required": true,
                        "schema": {
                            "type": "string"
                        },
                        "description": "Enter your Apify token here"
                    }
                ],
                "responses": {
                    "200": {
                        "description": "OK"
                    }
                }
            }
        },
        "/acts/trovevault~eu-withdrawal-button-auditor/runs": {
            "post": {
                "operationId": "runs-sync-trovevault-eu-withdrawal-button-auditor",
                "x-openai-isConsequential": false,
                "summary": "Executes an Actor and returns information about the initiated run in response.",
                "tags": [
                    "Run Actor"
                ],
                "requestBody": {
                    "required": true,
                    "content": {
                        "application/json": {
                            "schema": {
                                "$ref": "#/components/schemas/inputSchema"
                            }
                        }
                    }
                },
                "parameters": [
                    {
                        "name": "token",
                        "in": "query",
                        "required": true,
                        "schema": {
                            "type": "string"
                        },
                        "description": "Enter your Apify token here"
                    }
                ],
                "responses": {
                    "200": {
                        "description": "OK",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/runsResponseSchema"
                                }
                            }
                        }
                    }
                }
            }
        },
        "/acts/trovevault~eu-withdrawal-button-auditor/run-sync": {
            "post": {
                "operationId": "run-sync-trovevault-eu-withdrawal-button-auditor",
                "x-openai-isConsequential": false,
                "summary": "Executes an Actor, waits for completion, and returns the OUTPUT from Key-value store in response.",
                "tags": [
                    "Run Actor"
                ],
                "requestBody": {
                    "required": true,
                    "content": {
                        "application/json": {
                            "schema": {
                                "$ref": "#/components/schemas/inputSchema"
                            }
                        }
                    }
                },
                "parameters": [
                    {
                        "name": "token",
                        "in": "query",
                        "required": true,
                        "schema": {
                            "type": "string"
                        },
                        "description": "Enter your Apify token here"
                    }
                ],
                "responses": {
                    "200": {
                        "description": "OK"
                    }
                }
            }
        }
    },
    "components": {
        "schemas": {
            "inputSchema": {
                "type": "object",
                "required": [
                    "startUrls"
                ],
                "properties": {
                    "startUrls": {
                        "title": "Start URLs",
                        "type": "array",
                        "description": "One or more public website URLs to audit. Use homepages for full-site triage, or specific checkout, account, subscription, terms, cancellation, or withdrawal pages when you already know the relevant flow.",
                        "items": {
                            "type": "object",
                            "required": [
                                "url"
                            ],
                            "properties": {
                                "url": {
                                    "type": "string",
                                    "title": "URL of a web page",
                                    "format": "uri"
                                }
                            }
                        }
                    },
                    "proxyConfiguration": {
                        "title": "Proxy Configuration",
                        "type": "object",
                        "description": "Proxy settings for browser visits. Leave disabled for open websites. Enable Apify Proxy when a target blocks datacenter traffic, shows regional EU behavior only from specific locations, or returns 403, 429, CAPTCHA, or bot-protection pages.",
                        "default": {
                            "useApifyProxy": false
                        }
                    },
                    "datasetId": {
                        "title": "Dataset ID (optional)",
                        "type": "string",
                        "description": "ID of an existing Apify dataset to append audit results to, in addition to the default run dataset. Use this for recurring compliance pipelines that combine multiple scheduled runs into one dataset. Leave blank to write only to the default run dataset."
                    },
                    "runId": {
                        "title": "Run ID (optional)",
                        "type": "string",
                        "description": "ID of an upstream audit, client job, or scheduled workflow to associate with results. The actor copies this value into output rows so external systems can connect withdrawal-button findings to their own pipeline. Leave blank for standalone runs."
                    }
                }
            },
            "runsResponseSchema": {
                "type": "object",
                "properties": {
                    "data": {
                        "type": "object",
                        "properties": {
                            "id": {
                                "type": "string"
                            },
                            "actId": {
                                "type": "string"
                            },
                            "userId": {
                                "type": "string"
                            },
                            "startedAt": {
                                "type": "string",
                                "format": "date-time",
                                "example": "2025-01-08T00:00:00.000Z"
                            },
                            "finishedAt": {
                                "type": "string",
                                "format": "date-time",
                                "example": "2025-01-08T00:00:00.000Z"
                            },
                            "status": {
                                "type": "string",
                                "example": "READY"
                            },
                            "meta": {
                                "type": "object",
                                "properties": {
                                    "origin": {
                                        "type": "string",
                                        "example": "API"
                                    },
                                    "userAgent": {
                                        "type": "string"
                                    }
                                }
                            },
                            "stats": {
                                "type": "object",
                                "properties": {
                                    "inputBodyLen": {
                                        "type": "integer",
                                        "example": 2000
                                    },
                                    "rebootCount": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "restartCount": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "resurrectCount": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "computeUnits": {
                                        "type": "integer",
                                        "example": 0
                                    }
                                }
                            },
                            "options": {
                                "type": "object",
                                "properties": {
                                    "build": {
                                        "type": "string",
                                        "example": "latest"
                                    },
                                    "timeoutSecs": {
                                        "type": "integer",
                                        "example": 300
                                    },
                                    "memoryMbytes": {
                                        "type": "integer",
                                        "example": 1024
                                    },
                                    "diskMbytes": {
                                        "type": "integer",
                                        "example": 2048
                                    }
                                }
                            },
                            "buildId": {
                                "type": "string"
                            },
                            "defaultKeyValueStoreId": {
                                "type": "string"
                            },
                            "defaultDatasetId": {
                                "type": "string"
                            },
                            "defaultRequestQueueId": {
                                "type": "string"
                            },
                            "buildNumber": {
                                "type": "string",
                                "example": "1.0.0"
                            },
                            "containerUrl": {
                                "type": "string"
                            },
                            "usage": {
                                "type": "object",
                                "properties": {
                                    "ACTOR_COMPUTE_UNITS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATASET_READS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATASET_WRITES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "KEY_VALUE_STORE_READS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "KEY_VALUE_STORE_WRITES": {
                                        "type": "integer",
                                        "example": 1
                                    },
                                    "KEY_VALUE_STORE_LISTS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "REQUEST_QUEUE_READS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "REQUEST_QUEUE_WRITES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATA_TRANSFER_INTERNAL_GBYTES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATA_TRANSFER_EXTERNAL_GBYTES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "PROXY_RESIDENTIAL_TRANSFER_GBYTES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "PROXY_SERPS": {
                                        "type": "integer",
                                        "example": 0
                                    }
                                }
                            },
                            "usageTotalUsd": {
                                "type": "number",
                                "example": 0.00005
                            },
                            "usageUsd": {
                                "type": "object",
                                "properties": {
                                    "ACTOR_COMPUTE_UNITS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATASET_READS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATASET_WRITES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "KEY_VALUE_STORE_READS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "KEY_VALUE_STORE_WRITES": {
                                        "type": "number",
                                        "example": 0.00005
                                    },
                                    "KEY_VALUE_STORE_LISTS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "REQUEST_QUEUE_READS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "REQUEST_QUEUE_WRITES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATA_TRANSFER_INTERNAL_GBYTES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATA_TRANSFER_EXTERNAL_GBYTES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "PROXY_RESIDENTIAL_TRANSFER_GBYTES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "PROXY_SERPS": {
                                        "type": "integer",
                                        "example": 0
                                    }
                                }
                            }
                        }
                    }
                }
            }
        }
    }
}
```
