Sub-processor list example · checked
A GDPR sub-processor list, drafted from code
This is the sub-processor list for a small Next.js app on Vercel with a Postgres database, Stripe, email, an LLM API, error monitoring and product analytics: 9 vendors, each with what it does, the personal data it gets, where it runs and its DPA. Every row was found in the app's code, and says where. The app is synthetic, written for this example; your list will differ.
| Sub-processor | Purpose | Personal data | Location | DPA |
|---|---|---|---|---|
| Vercelfrom vercel.json:1 (config); package.json:15 (dependency @vercel/analytics); 1 more | Hosting and serverless functions; Web analytics (Vercel Analytics) | Everything your app serves and handles while it runs there, including request logs with IP addresses | fra1 (Frankfurt) | DPA |
| Amazon Web Servicesfrom infra/main.tf:2 (Terraform provider "aws"); package.json:12 (dependency @aws-sdk/client-s3); 1 more | Cloud infrastructure; File storage (S3) | Depends on the services you use: anything you store or process there | eu-central-1 | DPA |
| Neonfrom package.json:13 (dependency @neondatabase/serverless); src/lib/db.ts:1 (import @neondatabase/serverless); 1 more | Serverless Postgres database | All personal data your app stores | to confirm | DPA |
| Stripefrom package.json:22 (dependency stripe); src/lib/billing.ts:1 (import stripe); 3 more | Payments and billing | Customers' names, email and billing addresses; card details, which the vendor holds | to confirm | DPA |
| Resendfrom src/lib/email.ts:3 (API host api.resend.com); .env.example:8 (env name RESEND_API_KEY); 1 more | Transactional email | Recipients' names and email addresses, and message content | to confirm | DPA |
| Anthropicfrom package.json:11 (dependency @anthropic-ai/sdk); src/lib/ai.ts:1 (import @anthropic-ai/sdk); 1 more | LLM inference (Claude) | Prompts and outputs, and any personal data users or your app put in them | to confirm | DPA |
| Sentryfrom package.json:14 (dependency @sentry/nextjs); sentry.server.config.ts:1 (import @sentry/nextjs); 2 more | Error and performance monitoring | IP addresses, user IDs or emails attached to errors, and request data in stack traces | to confirm | DPA |
| PostHogfrom package.json:19 (dependency posthog-js); src/app/posthog.tsx:2 (import posthog-js); 3 more | Product analytics and session replay | Usage events, device and browser data, IP addresses and user IDs | to confirm | DPA |
| Slackfrom .env.example:12 (env name SLACK_WEBHOOK_URL); src/lib/alerts.ts:3 (env name SLACK_WEBHOOK_URL) | Alerts and notifications to Slack | Whatever your alerts and notifications contain | to confirm | DPA |
Drafted on 9 October 2026. Under each name, the file and line the row came from. "To confirm" is a region the code does not say: it is a setting in that vendor's account.
What the draft could not know
- The legal entity each contract is with, and the transfer mechanism for processing outside the EEA. Both are in the vendor's DPA.
- Whether each vendor really gets personal data. Slack came from env variable names only; confirm they are used in production.
- Vendors outside the code: the workspace your team emails customers from, the support desk, the CRM, payroll.
Set apart: probably not a sub-processor
GitHub: Usually not a sub-processor: CI runs your code, not your customers' data, and with Sign in with GitHub, GitHub is the controller of its own users' accounts. List it if a job reads production data or you send customer data to its API.
Draft yours
The rows above are what a scan of the app's package.json, imports, API hosts, example env file, vercel.json and Terraform found.
$ grep -rnE --include=package.json --exclude-dir=node_modules '"(@ai-sdk/amazon-bedrock|@ai-sdk/anthropic|@ai-sdk/azure|@ai-sdk/cohere|@ai-sdk/gateway|@ai-sdk/google|@ai-sdk/google-vertex|@ai-sdk/groq|@ai-sdk/mistral|@ai-sdk/openai|@ai-sdk/togetherai|@ai-sdk/xai|@algolia/[^"]*|@amplitude/[^"]*|@anthropic-ai/bedrock-sdk|@anthropic-ai/claude-agent-sdk|@anthropic-ai/sdk|@anthropic-ai/vertex-sdk|@auth0/[^"]*|@aws-sdk/[^"]*|@aws-sdk/client-bedrock-runtime|@aws-sdk/client-dynamodb|@aws-sdk/client-s3|@aws-sdk/client-ses|@aws-sdk/client-sesv2|@aws-sdk/client-sqs|@aws-sdk/lib-dynamodb|@aws-sdk/lib-storage|@axiomhq/[^"]*|@azure/[^"]*|@azure/openai|@azure/storage-blob|@clerk/[^"]*|@cloudflare/next-on-pages|@cloudinary/[^"]*|@datadog/[^"]*|@deepgram/sdk|@discordjs/[^"]*|@elevenlabs/[^"]*|@google-cloud/[^"]*|@google-cloud/bigquery|@google-cloud/storage|@google-cloud/vertexai|@google/genai|@google/generative-ai|@helicone/[^"]*|@huggingface/inference|@intercom/[^"]*|@langfuse/[^"]*|@lemonsqueezy/[^"]*|@libsql/client|@liveblocks/[^"]*|@logtail/[^"]*|@mistralai/mistralai|@mux/[^"]*|@neondatabase/[^"]*|@netlify/[^"]*|@openai/agents|@opennextjs/cloudflare|@openrouter/ai-sdk-provider|@openrouter/sdk|@paddle/[^"]*|@pinecone-database/[^"]*|@planetscale/database|@plausible-analytics/tracker|@posthog/[^"]*|@prisma/adapter-neon|@segment/[^"]*|@sendgrid/[^"]*|@sentry/[^"]*|@slack/[^"]*|@stripe/[^"]*|@supabase/[^"]*|@trigger\.dev/[^"]*|@tursodatabase/[^"]*|@uploadthing/[^"]*|@upstash/[^"]*|@vercel/analytics|@vercel/blob|@vercel/edge-config|@vercel/functions|@vercel/kv|@vercel/og|@vercel/postgres|@vercel/speed-insights|@workos-inc/[^"]*|ably|algoliasearch|analytics-node|apify|apify-cli|apify-client|auth0|aws-cdk-lib|aws-sdk|cloudflare|cloudinary|cohere-ai|crisp-api|crisp-sdk-web|dd-trace|discord\.js|elevenlabs|firebase|firebase-admin|firebase-tools|ga-4-react|groq-sdk|inngest|intercom-client|langfuse|langfuse-langchain|langfuse-vercel|loops|mailgun-js|mailgun\.js|mixpanel|mixpanel-browser|mongodb|mongoose|netlify-cli|next-axiom|next-cloudinary|next-plausible|openai|plausible-tracker|posthog-js|posthog-node|postmark|pusher|pusher-js|raven-js|react-ga4|react-instantsearch|react-use-intercom|replicate|resend|stripe|supabase|together-ai|twilio|uploadthing|vercel|wrangler)"[[:space:]]*:' .
Run it from the repository root; it needs nothing installed and sends nothing anywhere. It finds every dependency, in every package.json, that names one of the 69 vendors in the table below. It cannot see a vendor your code calls without an SDK (an API host in a string, a webhook URL), or one named only by an env variable; check those by hand.
Not legal advice. Whether a vendor is your sub-processor depends on the personal data you send it and on your contracts; a list drafted from code is a draft for you to confirm, row by row. It also misses vendors outside the code (your workspace, support desk, CRM, payroll): add those by hand.
A hosted, dated sub-processor page that emails your customers each change is not built.
It would publish the list you approved at a public URL, keep every change with its date, email your subscribers the notice with the day their objection window closes, and record who was told when. agentcheck runs scheduled checks of endpoints today; this is not one of them. If you would pay for it, say so with one click. The click is counted; nothing else is sent or stored.
Counted. Thank you; nothing else was sent.Sources
- GDPR Art. 28: processor (read 9 October 2026)
- EDPB Opinion 22/2024 on sub-processing chains (Hogan Lovells summary) (read 9 October 2026)
- GitHub subprocessors: notice at least 30 days in advance (read 9 October 2026)
- Stripe's DPA: 30 days to object, then deemed accepted (ConductAtlas) (read 9 October 2026)
subprocessors is a free tool from agentcheck, which runs scheduled checks of your endpoints and alerts you when an answer changes. Vendor links come from each vendor's own pages, read on the day shown.