When you sell to EU businesses you are a processor acting on your customer's instructions, and they are the controller. Article 28 requires a written contract, and your customers will ask for one before signing. This page covers the register you must publish and the clauses you must be able to meet.
Your dual role
| Relationship | Your role | Their role |
|---|---|---|
| End user signing up directly (B2C) | Controller | Data subject |
| Business customer whose employees use your product | Processor | Controller |
| You ↔ Supabase, Stripe, OpenRouter, AI model providers | Controller (or processor) | Subprocessor |
Both roles apply simultaneously in a B2B deployment. The ROPA documents you as controller; this page covers you as processor.
Subprocessor register
Publish this list and keep it current. Article 28(2) requires informing controllers of intended changes so they can object — you cannot do that if you never published a baseline.
| Subprocessor | Purpose | Data | Location | Required |
|---|---|---|---|---|
| Supabase | Database, auth, storage | All application data | Per project region | Yes |
| Stripe | Payments | Billing identifiers, amounts | EU/US | If billing enabled |
| Vercel (or your host) | Application hosting | Request data in transit | Global edge | Yes |
| OpenRouter, Inc. | LLM gateway, routing, embeddings | Prompts, documents, request metadata | US; downstream regions vary | If AI_LLM_TRANSPORT=openrouter |
| OpenAI | LLM inference, embeddings | Prompts, documents | EU/US | If OPENAI_API_KEY set |
| Anthropic | LLM inference | Prompts | EU/US | If ANTHROPIC_API_KEY set |
| LLM inference | Prompts | EU/US | If GOOGLE_AI_API_KEY set | |
| Brevo or Mailjet | Transactional + marketing email | Email, name, message content | EU | Per EMAIL_PROVIDER |
| Upstash | Rate limiting | Hashed identifiers | Configurable | Production |
| Cloudflare | Turnstile, optional CDN | IP, request metadata | Global | If enabled |
The AI providers listed here must match AI_PROVIDER_DISCLOSURES in config/ai-act.ts and what /[locale]/ai-transparency renders. Three surfaces, one truth — if they disagree, the published notice is wrong somewhere.
Before launch, sign a DPA with each one you actually use. Most publish a standard DPA you accept in their dashboard; some require a signed copy. Accepting terms of service is not signing a DPA.
What customers will require
Art. 28(3) clauses
Your customer DPA must commit you to: process only on documented instructions; ensure personnel confidentiality; implement Art. 32 security measures; respect subprocessor rules; assist with data-subject requests; assist with Art. 32–36 obligations; delete or return data at contract end; and submit to audits.
The clauses that actually bite
| Clause | What the boilerplate gives you |
|---|---|
| Data-subject request assistance | GET /api/user/export-data (Art. 15/20) and the 30-day deletion queue (Art. 17). Note the export is per user, not per customer-workspace — if a controller asks for everything about their whole organisation, you will assemble it manually. |
| Breach notification to controller | Breach runbook. Art. 33(2) says "without undue delay" — no 72-hour grace for processors, and your DPA probably sets 24 or 48 hours. Check what you signed. |
| Deletion at contract end | process-account-deletions covers Storage objects and auth.users, not only rows. Billing records survive on a legal-obligation basis — say so in the DPA rather than promising deletion you will not perform. |
| Subprocessor change notice | Not automated. You need a mechanism (email list, changelog, status page) before you sign a contract requiring 30 days' notice. |
| Audit rights | Usually satisfied by a SOC 2 or ISO 27001 report. Without one, expect questionnaires. |
The AI clause you will be asked about
Enterprise buyers now ask specifically: is our data used to train models? You must be able to answer definitively for every configured provider.
OpenRouter mode sends zdr: true and data_collection: "deny" on every chat and embedding request, and requires routes that accept the supplied parameters. That is an enforced routing preference, not a substitute for a DPA or an independent guarantee about every upstream model host. Verify OpenRouter's policy and the returned upstream provider for your account.
Direct mode cannot create contractual ZDR from application code. Direct OpenAI calls set store: false and the SDK ZDR flag, but OpenAI ZDR eligibility is an organization/project control. Anthropic and Google retention similarly depends on the API product, account settings, region, and agreement. Record the evidence for every enabled direct provider; "we believe so" fails a procurement review.
International transfers
Prompts and account data reach US-based subprocessors. Your lawful transfer mechanism is normally Standard Contractual Clauses and/or the provider's Data Privacy Framework certification, both of which sit inside their DPA.
Article 46 also expects a transfer impact assessment. For most SaaS this is a short document recording which providers are involved, which mechanism applies, and what supplementary measures exist (encryption in transit and at rest, access controls, retention limits). Short is fine; absent is not.
Maintenance
Review when you add or remove a provider, change EMAIL_PROVIDER, enable a new LLM provider, or change hosting region. Every change here means: update this page, update AI_PROVIDER_DISCLOSURES, notify controllers per your DPA, and bump PRIVACY_POLICY_VERSION in config/compliance.ts.