Trust & security

What happens to your data

This page describes what the AeoMax code does today, including what is not built yet. It was last checked against the code on , and an automated test fails our build if the retention table, the subprocessor list or the AI engines drift from the code.

No SOC 2 report yet (dated roadmap below)Legal documents are templates under counsel reviewSecurity pack available on request

Your prompts go to AI model providers

AeoMax measures what AI engines say about your brand by asking them your tracked prompts. Those prompts describe your market and often name your brand and competitors, and they are sent to every AI engine you track. Each answer is then graded by Anthropic's model, which also receives your brand name, domain, competitor names and the brand facts you have confirmed. Account data, credentials, GA4 and Search Console data are never sent to model providers.

You

  • Your team, in the dashboard
  • Your servers, via an aeo_ API key
HTTPS

AeoMax

  • Web app (Vercel)
  • API and query worker (Railway)
  • Neon · ClickHouse Cloud · Upstash
Prompt text; answers plus brand context for grading

AI engines

  • OpenAI
  • Anthropic
  • Perplexity
  • Google
  • Microsoft (Azure OpenAI) (if enabled)
  • xAI (if enabled)
  • DeepSeek (if enabled)
  • Groq (if enabled)

Google AI Overviews results are read by a headless browser that searches your prompt on google.com, optionally through Bright Data's proxy network. When grounded mode is on, ChatGPT, Claude, Gemini and Grok also run your prompt through their own vendor's web search. Whether a provider may keep or train on API traffic is set by that provider's API terms.

Subprocessors

Built from the providers the code actually calls. “When configured” means the code skips that provider unless the deployment turns it on.

Infrastructure

ProviderUsed forData it receivesIn use
VercelHosts the web app and forwards /api/v1 requests to the APIAll dashboard traffic in transit, including session cookiesAlways
RailwayRuns the API and the query workerAll customer data while it is processedAlways
NeonPostgreSQL databaseNames and emails of users, workspace settings, brands, competitors, prompts, alert rules, hashed API keys, encrypted integration tokens, self-reported attribution answers, branch names, addresses and phone numbers, report recipients' and pitch prospects' email addresses, quotes from AI answers (battlecards, claim findings), your answer-endpoint documents, daily AI-sourced CRM pipeline totalsAlways
ClickHouse CloudAnalytics databaseFull AI answers and their scores and citations, changes between consecutive answers, AI cost events, AI-crawler hits, requests to your answer-endpoint hostnames, audit events, daily GA4 and Search Console metricsAlways
UpstashRedis for the job queuesScan jobs (workspace, brand and prompt ids), free-grader jobs (domain, brand name, prompts, result), webhook delivery idsAlways
CloudflareR2 object storage for the full-answer archive; the Turnstile bot check on the free grader; Cloudflare for SaaS custom hostnames and TLS certificates for brand answer endpoints; the public DNS-over-HTTPS resolver (cloudflare-dns.com) for answer-endpoint verification, which is used in every deployment, with no accountFull AI answer text (R2); grader visitors' browser signals (Turnstile); your answer-endpoint hostnames, and the requests to them and the public documents served on them, which pass through Cloudflare (Cloudflare for SaaS); the hostname being verified (DNS lookup)When configured

Identity, billing and email

ProviderUsed forData it receivesIn use
ClerkSign-in, sessions, SAML SSO connections and workspace invitationsNames, email addresses, sign-in metadata, SAML identitiesAlways
StripeSubscription billing and metered overage. Checkout and the billing portal are hosted by StripeBilling contact and company details; each closed billing period's overage units (meter events). Card details are entered on Stripe's pages and never reach AeoMaxWhen configured
ResendTransactional email: alert emails, weekly digests and scheduled reportsRecipient email addresses; brand names and metric values in the message; scheduled reports as a PDF (visibility, engines, top cited sources and competitors) or a share linkWhen configured

Monitoring

ProviderUsed forData it receivesIn use
SentryError monitoring for the API and the web appError reports: stack traces, request URL and method, browser detailsWhen configured
PostHogProduct analytics in the browser (production only)Page views and page leaves: URL, referrer, browser, device and the IP address PostHog records. Autocapture is off and client share links (/share/…) are never sentWhen configured

AI engines

ProviderUsed forData it receivesIn use
OpenAIChatGPT engine, with OpenAI's hosted web search when grounded mode is on; free graderYour tracked prompts, which name your market and often your brand and competitors (in grounded mode, OpenAI's web search runs on them); grader promptsAlways
AnthropicClaude engine, with Anthropic's web search tool when grounded mode is on; grading of every answer (LLM judge); prompt suggestions; battlecards (when enabled); checks of answers against your regulated-vertical rules (when you add rules)Your tracked prompts (in grounded mode, Claude's web search runs on them). For grading: each AI answer with your brand name, domain, competitor names and confirmed brand facts. For suggestions: brand name, domain and category. For battlecards: up to 12 head-to-head answers with their prompts, your brand and the competitor's names and domains. For rule checks: each answer (first 16,000 characters) with your brand name and your active rulesAlways
PerplexityPerplexity engine; free graderYour tracked prompts; grader promptsAlways
GoogleGemini engine, with Grounding with Google Search when grounded mode is on; Google Search for the AI Overviews engine (the worker's headless browser searches each prompt on google.com); GA4 Data API, Search Console API and Google OAuth for brands that connect them; PageSpeed Insights; Knowledge Graph Search; Places API (Place Details) for branch listings, when configuredYour tracked prompts (Gemini, Gemini's Google Search grounding, Google Search); requests for your own GA4 and Search Console data; your site URLs (PageSpeed); your brand name (Knowledge Graph); the Google Place ID of each branch listing you track (Places returns its public name, address and phone)Always
Microsoft (Azure OpenAI)Copilot engine, through an Azure OpenAI deploymentYour tracked promptsWhen configured
xAIGrok engine, with xAI's hosted web search when grounded mode is onYour tracked prompts (in grounded mode, xAI's web search runs on them)When configured
DeepSeekDeepSeek engineYour tracked promptsWhen configured
GroqMeta AI (Llama) engine. Groq is the default host; META_AI_BASE_URL can point to another OpenAI-compatible hostYour tracked promptsWhen configured
Bright DataResidential proxy for the Google AI Overviews engineThe Google searches made for your tracked prompts pass through its networkWhen configured

Public lookups

ProviderUsed forData it receivesIn use
RedditReads the public metadata (subreddit, title, score, comment count, posted date) of Reddit threads that AI answers cited for your brand, through the Reddit Data API on app-only OAuth. No account of yours is usedThe ids of public Reddit threads that AI answers cited for your prompts. No customer data is sentWhen configured
Wikimedia (Wikidata)Entity lookups for the Brand Entity Passport. No account is usedYour brand nameAlways
Trustpilot (Business API)Reads your brand's public Trustpilot profile — rating, review count and the newest review's date — for the review supply chain report. Needs a paid Trustpilot Business accountYour brand's domain, to resolve the profileWhen configured
The websites AI answers citeAnswer Provenance fetches a cited page (and its robots.txt) to check which sentences of one archived answer came from it. On demand only, capped and cached, and off unless PROVENANCE_ENABLED is setNone. A plain GET for the public page, identified by PROVENANCE_USER_AGENTWhen configured

Destinations you configure yourself (the AeoMax Slack app, webhooks, Zapier, Make and n8n, your SIEM, your warehouse bucket, Linear, the AeoMax GitHub App, HubSpot) receive data only on your instruction and are not AeoMax subprocessors.

Retention

The ClickHouse periods are the TTLs set in the schema code. There is no self-serve deletion yet: data kept “for the life of the workspace” is deleted when you ask us in writing.

WhereDataKept for
ClickHouseAI answers and their scores, citations and grading24 months from the query date. Inside that, the answer text is blanked after the workspace's answer-retention period (default 90 days), but only where the deployment sets ANSWER_RETENTION_ENFORCE=true. Rows written before per-workspace retention existed are kept for the full 24 months.
ClickHouseDaily visibility snapshots per brand and engineNo automatic expiry: kept for the life of the workspace and deleted on request
ClickHouseDaily visibility snapshots per brand, engine and marketNo automatic expiry: kept for the life of the workspace and deleted on request
ClickHouseAnswer changes between consecutive runs of a prompt: the added and removed answer text, and mention, position, sentiment and citation moves90 days from the later answer's query date. Inside that, the changed answer text is blanked once the earlier answer passes the workspace's answer-retention period, but only where the deployment sets ANSWER_RETENTION_ENFORCE=true.
ClickHouseAI cost per scan12 months
ClickHouseDaily AI cost totals per workspace (derived from cost events)No automatic expiry: kept for the life of the workspace and deleted on request. Not expired together with cost_events
ClickHouseAI-crawler hits you send us (bot, path, user agent)24 months
ClickHouseBylines read from the public news pages AI answers cited for your brand (page URL, author name, publication date)24 months from the query date of the answer that cited the page
ClickHouseRequests to your brand answer-endpoint hostnames (hostname, path, user agent, bot)24 months
ClickHousePublished AI Visibility Index rankings: one score and rank per listed company per day, with the per-engine grounded/from-memory mode. No prompt, answer, workspace or brand columnNo automatic expiry: kept for the life of the workspace and deleted on request. Removing a listing stops new rows; the historical rows are deleted on request
ClickHouseIndustry benchmark percentiles: p25/p50/p75/p90 and the cohort size per industry, engine and day. Aggregate only — no individual score is stored, and a cohort below the configured minimum is never shownNo automatic expiry: kept for the life of the workspace and deleted on request
ClickHouseAnswer provenance: each sentence of an analysed answer with the cited URL and source passage it matches, plus whether each cited page could be read at all, written only when someone runs the check on one answer12 months from the analysed answer's query date
ClickHouseAudit log (append-only)24 months
ClickHouseDaily GA4 AI-referral sessions, conversions and revenueNo automatic expiry: kept for the life of the workspace and deleted on request
ClickHouseDaily GA4 AI-referral sessions, conversions and revenue per landing page18 months
ClickHouseModelled split of daily AI-referral revenue across the prompts whose answers cited each landing page18 months
ClickHouseDaily Search Console queries and pages18 months
ClickHouseDaily on-site search terms (GA4)18 months
PostgresAccounts, workspace and brand settings, prompts, competitors, branch locations, alert rules, report schedules and their recipients, pitch prospects, API keys, integrations, answer-endpoint hostnames and documentsNo automatic expiry: kept for the life of the workspace and deleted on request
PostgresConnections to Linear, GitHub and HubSpot (OAuth tokens encrypted) and Answer Deploy targetsNo automatic expiry: kept for the life of the workspace and deleted on request. Disconnecting Linear marks the connection revoked and clears its tokens; disconnecting GitHub marks it revoked (it holds no token); disconnecting HubSpot deletes the connection and its tokens; removing a deploy target deactivates it
PostgresScan, recommendation and work history, including impact measurements, Linear issue links, Answer Deploy history (the llms.txt proposed), entity-consistency checks and daily AI-sourced CRM pipeline totalsNo automatic expiry: kept for the life of the workspace and deleted on request. The CRM pipeline history stays after HubSpot is disconnected
PostgresCitation lift experiments: their name, hypothesis and window, and which of your prompts sat in the treatment and holdout armsNo automatic expiry: kept for the life of the workspace and deleted on request. No answer text is copied here — the arms are joined to the answers in ClickHouse at read time, so an experiment's numbers fade as those answers expire
PostgresCitation outreach targets: the host, its stage, your notes and due date, and the publisher contact name and email you enteredUntil you delete them (per-row delete in the app, which is a permanent delete), or until an operator deletes the workspace or brand on request. Contact name and email are personal data you supply about a third party: delete the row once the outreach is over if you have no ongoing basis to keep them
PostgresQuotes and claims taken from AI answers: battlecards and the false-claim ledger with its per-answer evidenceNo automatic expiry: kept for the life of the workspace and deleted on request. A battlecard is overwritten each time it is rebuilt. The answer-retention setting blanks answer text in ClickHouse only; it does not remove these quotes
PostgresRegulated-vertical rule checks: your rules, the findings (verbatim answer quotes) and each finding's review trailNo automatic expiry: kept for the life of the workspace and deleted on request. Rules are retired, not deleted. The review trail is append-only: no code updates or deletes it, and the database refuses a direct delete of a trail entry or a finding. Findings and their trails are deleted only together with their brand or workspace
PostgresUsage metering per billing period, the prepaid-credit ledger and the history of plan and overage-billing changes (both append-only)No automatic expiry: kept for the life of the workspace and deleted on request. Stripe keeps its own invoice records
PostgresDelivery and access logs: webhook deliveries, share-link views (hashed IP, user agent), events for polling, alert firings, scheduled-report deliveriesNo automatic expiry: kept for the life of the workspace and deleted on request. No automated purge yet
PostgresSelf-reported attribution answers from your visitors (emails and phone numbers masked at ingest)Until you delete them (per-row delete in the app, which is a permanent delete), or until an operator deletes the workspace or brand on request. Deleting a brand in the app only hides it
PostgresFree-grader submissions (domain, optional email, hashed IP, report) and referralsNo automatic expiry; deleted on request
PostgresThe shared news-outlet dictionary (public domains, no customer data) and the community threads cited for a brand (public Reddit thread metadata)Outlets are kept indefinitely; a brand's tracked threads are deleted with the brand. No automatic expiry: kept for the life of the workspace and deleted on request
PostgresModel version registry and detected model changes (engine metadata and operator notes, no customer data)Kept indefinitely
PostgresPublic AI Visibility Index: the industry taxonomy and the companies listed on it (name and domain only)No automatic expiry: kept for the life of the workspace and deleted on request. A brand is listed only while its owner keeps the opt-in on; turning it off removes the listing. Deleting a brand or closing the workspace also takes the listing off the public page
Postgres"This is my brand" claims submitted from public index pages (claimant email, their note, the decision)No automatic expiry; deleted on request, and together with the listing they name
PostgresDomain-ownership verification for the public score badge, and the badge's own settingsNo automatic expiry: kept for the life of the workspace and deleted on request. The verification token is public by design (you publish it in DNS or your page); deleting the brand or closing the workspace stops the badge being served, and both rows go with the brand's data
PostgresReview-network profiles for your brands (rating, review count, newest review date), their daily rating history, and the review passages AI answers were found quotingNo automatic expiry: kept for the life of the workspace and deleted on request. Removing a profile deletes its rating history with it; deleting a brand deletes all three
PostgresAgency partner program: the negotiated plan limits for a workspace, partner applications (company name, contact email, website) and public directory listingsNo automatic expiry: kept for the life of the workspace and deleted on request. A workspace's negotiated limits and its directory listing are deleted with the workspace; an application is kept as the record of the review decision
Object storageFull-answer archive in object storage, one prefix per retention tier (answers/90d/…), when configuredOne bucket lifecycle rule per retention tier (answers/90d/ expires after 90 days). Operations sets the rules on the bucket; the code does not create them.
RedisQueue jobsScan jobs: 1 day after completion, 7 days after failure. Free-grader jobs: 1 hour after completion, 1 day after failure.

Security controls in place

Authentication

Dashboard sign-in through Clerk. Every API route requires a session, an aeo_ API key, a signed request or an explicit public marker, and a test enforces this.

Limit: A local-development auth bypass (DEV_AUTH_BYPASS) exists in the code. It is refused whenever NODE_ENV=production.

Single sign-on and provisioning

SAML SSO through Clerk, verified email-domain claims, optional SSO enforcement, and SCIM 2.0 user and group provisioning. A deprovisioned user's next request is refused.

Limit: Needs Clerk's Enterprise SSO add-on, set up per customer connection.

Access control

Roles: admin, member, viewer and client. Members can optionally be limited to specific brands, with a role per brand. A brand-limited member cannot create or change API keys, webhooks or workspace-wide integrations (Slack, Linear, GitHub, HubSpot), or rename or deactivate the workspace, because those cover every brand. Operator surfaces — the ones that read across tenants — are separate from all of this: they need the operator key, and the screens that proxy it serve only named AeoMax staff (ADMIN_CLERK_USER_IDS), never a customer's own admin role.

Limit: API keys cover the whole workspace (read or read_write). Brand scoping applies to people, not keys.

Audit log

Every authenticated dashboard write is recorded: actor, action, resource and allow-listed before/after fields. The client IP is stored as a salted hash (AUDIT_IP_SALT), or not at all. Append-only in ClickHouse for 24 months, with optional streaming to your SIEM.

Limit: Requests made with API keys are not yet recorded.

API keys

Random 192-bit aeo_ keys, stored only as SHA-256 hashes and shown once. Optional expiry, revocable.

Secrets at rest

Slack, Search Console, SIEM, warehouse, Linear and HubSpot credentials are encrypted with AES-256-GCM before they are stored.

Limit: Webhook signing secrets are stored unencrypted, because signing needs the raw key.

Outbound webhooks

Standard Webhooks HMAC-SHA256 signatures on every webhook we send — workspace endpoints, Zapier/Make/n8n hooks and per-alert webhook URLs — with event ids for de-duplication and retries with backoff. Deliveries to private, loopback and cloud-metadata addresses are refused at connect time.

Limit: Slack incoming webhooks and SIEM destinations are not signed: they authenticate with the destination's own credential (the secret Slack URL, a Splunk HEC token, a Datadog API key, or a bearer token on a SIEM webhook).

Rate limiting

200 requests per minute per client IP on the API, read from the leftmost X-Forwarded-For entry set by the edge.

Limit: Counted in memory per API instance, not across instances. A caller that reaches the origin directly, bypassing the edge, can present another address. Requests to a verified brand answer-endpoint hostname are answered from an in-process cache ahead of the limiter and are not counted.

Browser security headers

X-Frame-Options DENY, nosniff, a strict Referrer-Policy and a Permissions-Policy on every web page. Helmet on the API.

Retention

ClickHouse TTLs per table (see the schedule), and a per-workspace answer-retention setting (default 90 days).

Limit: Answer-retention enforcement is opt-in per deployment (ANSWER_RETENTION_ENFORCE).

Internal operations

Internal admin endpoints require a shared operator key.

Limit: One shared key: no per-operator identity and no audit trail for operator actions.

Not in place yet

  • No SOC 2 report yet. The dated roadmap is below.
  • No independent penetration test yet.
  • Requests made with API keys are not recorded in the audit log.
  • Internal admin endpoints share one operator key, with no per-operator identity.
  • No self-serve account deletion. Deletion is done on written request.
  • Postgres history and delivery logs have no automated purge.

SOC 2 roadmap

Target dates. A SOC 2 report is issued by an independent auditor; we do not have one today.

  1. Security pack published: subprocessors, data flow, retention, DPA templateDone
  2. Named security owner appointed; compliance-automation platform selectedPlanned
  3. Policies adopted (information security, access, change, incident response, vendor, continuity); audit log covers API-key requests; per-operator admin accessPlanned
  4. Independent penetration test; findings fixed by 2027-01-31Planned
  5. SOC 2 auditor engaged; readiness assessment completePlanned
  6. SOC 2 Type I report (Security criteria)Planned
  7. SOC 2 Type II observation window opens (six months)Planned
  8. SOC 2 Type II reportPlanned

Need the DPA template, the data-flow diagram or answers to a security questionnaire? Ask your AeoMax contact for the security pack.