EU Data Act · Arts. 25–32
Switching and data export
How to take your data and your automation elsewhere — the procedure, the export format, where your data sits and who could ask for it, and what it costs (nothing).
draft 2026-10-04 · unreviewed
1. In short
- There are no switching, export, egress or early-termination charges. Downloading the export is limited to 10 times per hour per organisation, only to protect the service.
- Your automation already speaks an open protocol (Chrome DevTools Protocol), so it moves to another provider by changing one URL.
- Organisation owners and admins can download everything we hold about the organisation, as one JSON document, at any time — while the service runs and afterwards.
- After the service ends the export stays available for at least 30 days; then the data is erased.
2. Procedure and timeline (Art. 26(a))
- Request. Close your organisation in the dashboard, or email [PLACEHOLDER: support email — DSA Art. 12 point of contact]. Say whether you switch to another provider, move to your own infrastructure, or want your data erased — and whether you need a transitional period.
- Notice period. Your plan keeps running until the end of the billing period you have paid for — at most a month, never more than the two months the Data Act allows. Renewal stops with the request.
- Transitional period (optional). On request we keep the service running unchanged for up to 30 more days at the standard monthly fee, pro rata, and help you and anyone you authorise. If that is technically unfeasible we tell you within 14 working days, with reasons and an alternative of at most 7 months. You may extend the transitional period once.
- Service end. Your workers are destroyed, and we confirm the end of the contract by email.
- Retrieval period. For at least 30 days you can still sign in and download the export.
- Erasure. Then we erase your exportable data. Kept only because the law requires or allows it: invoice and booking records (statutory retention), DSA decision records, and the connection log until its own retention ends (see the privacy policy). An open abuse case or an AUP / sanctions suspension postpones the erasure until it is decided. Backups expire within 15 days. On request we confirm the erasure in writing.
You can withdraw a close request until the service ends; the renewal then stays off until you turn it back on in the billing portal.
3. Getting your data out
In the dashboard, as an owner or admin, download the export; or, while signed in, call GET /api/dashboard/export. You get one JSON document (flatbrowser.export, format version 1) containing every category listed in the register below. Large histories are streamed in full, not truncated.
Known restrictions and technical limitations
- Browser state does not persist: cookies, local storage, cache and downloads live in a temporary profile that is deleted when each session ends. There is nothing browser-side to port — keep login state on your side (for example Playwright storageState).
- Screenshots, PDFs and HTML from the REST API are returned in the response and never stored, so they are not in the export.
- API-key secrets are never stored, so they cannot be exported. Create new keys at your new provider.
- Invoices are read from Stripe when you export (up to 1,000); they are also available in the billing portal.
- Exempt for security and trade-secret reasons (Terms §12a): key hashes, worker access tokens, host and network configuration, our hostname blocklist, capacity data, our internal audit log, and the identity of people who sent notices about you.
4. Format register (Art. 26(b))
The export is UTF-8 JSON (RFC 8259); timestamps are ISO 8601 in UTC; amounts are integers in the smallest currency unit. A JSON Schema (draft 2020-12) of the current version is published at /switching/export-schema.json. The browser interfaces themselves follow open specifications: the Chrome DevTools Protocol, WebSocket (RFC 6455) and JSON over HTTPS.
formatvalue
Always "flatbrowser.export".
formatVersionvalue
Version of this format (currently 1); raised only for incompatible changes.
exportedAtvalue
When the export was generated (ISO 8601, UTC).
notesvalue
Human-readable notes about what the export does not contain and why.
organizationrecord
Your organisation.
| field | type | meaning |
|---|---|---|
| id | string | Organisation ID. |
| name | string | Display name. |
| slug | string | URL-safe short name. |
| createdAt | timestamp | When the organisation was created. |
| suspendedAt | timestamp | null | When a current suspension started. |
| suspendedReason | string | null | Reason given for a current suspension. |
| closeRequestedAt | timestamp | null | When closing the organisation was requested. |
memberslist
People in the organisation, with their terms acceptance records.
| field | type | meaning |
|---|---|---|
| userId | string | User ID. |
| name | string | Display name. |
| string | Email address. | |
| role | string | owner, admin or member. |
| joinedAt | timestamp | When the user joined the organisation. |
| termsAcceptedAt | timestamp | null | When the user accepted the Terms, AUP and DPA. |
| termsVersion | string | null | Version of the documents accepted. |
| businessConfirmedAt | timestamp | null | When the user confirmed acting as a business (§14 BGB). |
invitationslist
Invitations sent to join the organisation.
| field | type | meaning |
|---|---|---|
| string | Invited address. | |
| role | string | null | Role offered. |
| status | string | pending, accepted, rejected or canceled. |
| createdAt | timestamp | When the invitation was sent. |
| expiresAt | timestamp | When the invitation expires. |
apiKeyslist
API-key metadata. Key secrets are never stored, so they cannot be exported.
| field | type | meaning |
|---|---|---|
| id | string | Key ID (referenced by sessions and the connection log). |
| name | string | Name you gave the key. |
| keyPrefix | string | First characters of the key, for recognising it. |
| createdAt | timestamp | When the key was created. |
| lastUsedAt | timestamp | null | When the key was last used. |
| revokedAt | timestamp | null | When the key was revoked. |
subscriptionrecord · may be null
Your plan and its billing state (null if you never subscribed).
| field | type | meaning |
|---|---|---|
| planCode | string | solo, team or scale. |
| status | string | trialing, active, past_due or canceled. |
| currentPeriodEnd | timestamp | null | End of the paid billing period. |
| cancelAtPeriodEnd | boolean | Whether the subscription ends at the period end. |
| pastDueSince | timestamp | null | When a renewal payment first failed. |
| stripeCustomerId | string | null | Stripe customer reference. |
| stripeSubscriptionId | string | null | Stripe subscription reference. |
| createdAt | timestamp | When the subscription record was created. |
workerslist
The browser workers provisioned for you.
| field | type | meaning |
|---|---|---|
| id | string | Worker ID. |
| status | string | provisioning, ready, draining, error or deleted. |
| slots | integer | Concurrent sessions the worker supports. |
| createdAt | timestamp | When the worker was created. |
| updatedAt | timestamp | Last status change. |
abuseDecisionslist
Decisions we took about notices concerning your organisation (DSA Art. 17). The notifier’s identity is never included.
| field | type | meaning |
|---|---|---|
| reference | string | Case reference. |
| kind | string | Type of notice. |
| status | string | Case status. |
| decision | string | null | The decision taken. |
| decidedAt | timestamp | null | When it was decided. |
| statementSentAt | timestamp | null | When the statement of reasons was sent. |
| receivedAt | timestamp | When the notice arrived. |
invoiceslist · may be null
Your invoices, read from Stripe at export time (up to 1,000). null with an "invoicesNote" if Stripe could not be reached; the invoices are also in the billing portal.
| field | type | meaning |
|---|---|---|
| id | string | Stripe invoice ID. |
| number | string | null | Invoice number. |
| status | string | null | draft, open, paid, uncollectible or void. |
| currency | string | ISO currency code, lower case. |
| subtotal | integer | Net amount in cents. |
| total | integer | Gross amount in cents. |
| amountPaid | integer | Amount paid in cents. |
| createdAt | timestamp | Invoice date. |
| periodStart | timestamp | Start of the billed period. |
| periodEnd | timestamp | End of the billed period. |
| hostedInvoiceUrl | string | null | Stripe-hosted invoice page. |
| invoicePdf | string | null | Invoice PDF link. |
sessionslist
Your browser sessions still in the session history.
| field | type | meaning |
|---|---|---|
| id | string | Session ID. |
| workerId | string | null | Worker that ran it. |
| hostId | string | null | Host that ran it. |
| apiKeyId | string | null | API key that created it. |
| status | string | starting, active, closed or failed. |
| source | string | ws (born from a WebSocket connection) or rest. |
| startedAt | timestamp | Start time. |
| endedAt | timestamp | null | End time. |
| closeReason | string | null | Why it ended. |
| clientIp | string | null | IP address of the client that created it. |
| egressIp | string | null | Public IP address it used for outbound traffic (null behind your own proxy). |
usageEventslist
Usage and traffic records, e.g. outbound bytes per worker and time window.
| field | type | meaning |
|---|---|---|
| id | string | Record ID. |
| type | string | Record type, e.g. egress_bytes. |
| quantity | integer | Amount (bytes for egress_bytes). |
| meta | object | null | Details: worker, host, received bytes, time window. |
| createdAt | timestamp | When the record was written. |
connectionLoglist
The connection-log entries about your organisation (Terms §6.1).
| field | type | meaning |
|---|---|---|
| id | string | Entry ID (a number, as a string). |
| at | timestamp | When it happened. |
| kind | string | session.start, session.attach, session.detach, session.end, rest.action, rest.blocked, egress.bytes, org.suspended, org.close_requested, org.close_withdrawn, or a billing event: billing.checkout_completed, billing.payment_reminder, billing.dispute, billing.dispute_closed, billing.fraud_warning, billing.refund, billing.tax_id_removed or billing.tax_id_unavailable. |
| apiKeyId | string | null | API key involved. |
| sessionId | string | null | Session involved. |
| workerId | string | null | Worker involved. |
| hostId | string | null | Host involved. |
| clientIp | string | null | IP address of the connecting client. |
| egressIp | string | null | Public IP address used for outbound traffic. |
| targetHost | string | null | Target hostname of a REST call. |
| bytes | integer | null | Bytes piped or sent. |
| detail | object | null | Kind-specific details (close reason, proxy host, outcome, …). |
Changelog
- Version 1 (2026-10-04): First published version.
5. Moving your automation
- Puppeteer, Playwright and any other CDP client: replace our WebSocket URL with your new provider’s, or go back to launching Chromium locally. No library of ours is involved.
- REST calls: our
/v1endpoints are documented in the REST reference, and the Browserless-style aliases accept Browserless request bodies, so moving between compatible providers is a base-URL change. - Proxies you configured are yours; take them with you.
- Nothing runs on your infrastructure, so there is nothing to uninstall.
6. Jurisdiction and government access (Art. 28)
Who and where
- The provider of the service is [PLACEHOLDER: registered legal name incl. legal form (GmbH / UG (haftungsbeschränkt))], established in Germany and subject to German and EU law.
- The ICT infrastructure — every server that runs the service and stores your data — is operated by Hetzner Online GmbH, a German company, in its data centres in Falkenstein and Nuremberg (Germany), Helsinki (Finland). It is subject to German and EU law (Finnish law as well for the Helsinki site).
- Encrypted database backups are stored with [PLACEHOLDER: offsite backup storage provider — name, seat and location (a separate account from Hetzner)] in the EU.
- Billing data is held by Stripe Payments Europe Ltd. (Ireland), part of a US group, under our controllership — it is not part of the service data described here.
Measures against unlawful third-country government access to non-personal data (Arts. 28(1)(b) and 32)
- EU-only infrastructure, enforced in software: the provisioning system refuses to order servers outside the EU locations above.
- No subprocessor established or controlled outside the EU holds service data.
- Encryption in transit for all external traffic; backups are encrypted before they leave the server, with a key the storage provider does not hold.
- Every request from a third-country authority is reviewed for legality: we comply only where an international agreement such as a mutual legal assistance treaty, or the conditions of Art. 32(2) and (3), allow it, and we disclose only the minimum the request requires.
- Before complying we inform you of the request, unless it serves law enforcement and informing you would endanger that purpose, for as long as that is the case (Art. 32(5)).
7. Fees (Art. 29)
The standard fees are the monthly plan prices published on the pricing section and in the billing terms. There are no switching charges, no data-export or egress charges and no early-termination penalties — not now, and not after 12 January 2027 when the Data Act forbids them. During an optional transitional period only the standard monthly fee applies, pro rata.
8. Questions
Switching and export: [PLACEHOLDER: support email — DSA Art. 12 point of contact]. Legal questions about these clauses: [PLACEHOLDER: legal / DPA contact email].