Security & trust
An auditor you can hand your data to.
Spendassay sits between your AI spend and your engineering systems, so trust is not a feature here — it is the whole job. This page states the posture from the code that enforces it: what we read, what we refuse to read, how tenants are isolated, and exactly where our SOC 2 work stands today. No badges we haven't earned.
Posture at a glance
Stated from the code, not a logo
SOC 2, as of 26 July 2026: 20 of 34 controls met and code-enforced, with 9 partial and 5 deferred. The full control-by-control readiness report is available on request.
The readiness report — the full control-by-control matrix, including the 9 partial and 5 deferred controls — is available on request for a security review. We would rather show you the gaps than paper over them.
A DPA is available on request — email security@spendassay.com.
The bright lines
Hard limits, enforced below the UI
These are architectural constraints, not settings a customer or a salesperson can turn off. They survive a works-council review and a security questionnaire because they are enforced in the data layer.
The audit is read-only — it never writes to your systems
Spendassay connects through read-scoped access to billing, vendor usage data, and Git/CI. It cannot merge a PR, revoke a seat, change a license, or write to any system you connect. Recoveries run as guided step-by-step instructions your own admins execute — approved, tracked, and audited in-product, with zero vendor API writes. Routing and throttling are Control, a separate opt-in plan (private beta) that runs on your own provider keys. It is never enabled by default, and it never touches the audit's connectors.
Metadata only — never your code or content
The audit reads counts, timestamps, churn, and review events — the statistics measurement actually needs. It does not read, store, or transmit source code, diffs, file contents, prompts, or message bodies. Identity connectors read sign-in metadata only: who signed in to which app, when — never email or file content.
Team-level only, minimum cohort of eight
No productivity figure renders for a group smaller than eight people — a hard floor enforced below the UI, not a setting. There are no per-person rankings and no surveillance leaderboards. Below the floor we suppress the number rather than risk re-identifying an individual from an aggregate.
Tenant isolation enforced by the database
Every request is served by a non-owner database role, so the database itself keeps each customer's data apart: a policy filters every table to the caller's organization, and one tenant's query cannot reach another tenant's rows even if application code forgot a filter. Isolation is a database guarantee, not a convention in the app layer.
The request path
The optional gateway, and what it does not do
Everything above describes the audit, which connects read-only to systems you already run. The request-path proxy is a different product with a different posture, so it gets its own bright lines rather than sheltering under the audit's. It is open source, so you can read every line of it before you run it. This is the posture behind Spendassay Meter, which is observe-only and never routes or throttles at all.
Separate, and opt-in
The audit needs no gateway. The proxy is a distinct component you run yourself — it only ever sees the traffic you deliberately point at it, and running it changes nothing about the audit's read-only posture.
Nothing is stored
The proxy reads token counts, model, latency, and your tags off each request. It never stores your prompts or completions. Request and response bodies pass through and are discarded; only the usage numbers and tags are sent to Spendassay.
Verbatim unless you say otherwise
The gateway forwards your request verbatim unless you configure a routing rule. With routing configured it changes exactly one field — the model — and re-serializes every other key untouched.
You declare what's routine
Routing fires only on tags you set, never on an inspection of your prompt. There is no classifier reading your content to guess difficulty, which means no added latency, no extra inference hop, and a routing decision you can audit line by line.
It fails open
A malformed rule, an unparseable body, or a Spendassay outage all degrade to forwarding your original request. The proxy is designed so that its own failure cannot become your outage.
How your data is protected
The controls behind the claims
Credentials encrypted at rest
Connector credentials are AES-256-GCM encrypted with per-connection keys before they touch the database, validated on save, and health-checked before every sync. Disconnecting a provider wipes them.
CC6.7 · MetA log that can be added to but never changed or deleted
Sensitive actions — approvals, executions, role changes, redactions, connector syncs — land in an audit log the application role can only read and insert, never update or delete. Tamper-resistance is a database grant, not a code promise.
CC7.3 · MetAccess control + SSO
Sign in with Google or Microsoft Entra ID, or an email sign-in link. Roles gate every mutation; the demo tenant is genuinely read-only. Owners and admins manage members and connections.
CC6.1–6.3 · MetRead-only, with a time limit on every connection
Outbound calls to vendor APIs are read-scoped and carry hard timeouts, so a slow or hostile upstream can't hang a request or exfiltrate beyond its scope.
CC6.6 / A1.1 · MetAssurance
What we have not done yet, said plainly
Penetration testing
No external penetration test has been performed. We are not going to publish a target date for one, for the same reason we publish no SOC 2 Type I date: a compliance date with no engagement behind it is a promise we have not made. What exists today is in the controls above, and it is checkable — the tenant isolation is enforced by the database rather than by application code, and the audit log is append-only by grant. If a test is a requirement for your review, say so and we will scope one.
If something goes wrong
Every unhandled error in the application is captured with its request context and routed to the on-call engineer. Spendassay is small enough that on-call is one person rather than a rota, and that person is the one who built the system — which is worth more at this size than a tiered escalation path would be, and is the honest description either way. Report anything you find to security@spendassay.com.
Data lifecycle
What we hold, and how it leaves
What we store
Spend, seat, usage, and delivery facts, all put in the same format — metadata and aggregates. Names and emails live only in your people list and are redactable at any time. We do not store source code, diffs, or message content.
Person-level erasure
An admin can erase an individual (privacy P4.2): their provider aliases are deleted, their identifying fields are nulled, and a redaction timestamp drops them from every people-facing read — while aggregates keep their integrity, now anonymous. The action is audited and reversible only by re-linking, never by un-erasing.
Tenant deletion
Disconnect a provider and its stored credentials are wiped. On offboarding, deleting your organization cascades through every org-scoped table — users, facts, connections, imports, audit log — in a single transaction, leaving no residual tenant data.
What each connector reads
Scoped to the metadata the audit needs
Every connector is read-only and scoped to the smallest signal that answers the question. Below is the literal permission string each one requests, so you can pre-clear the grant before you authorize it rather than take an adjective on trust. These are read from the connector code and a test fails the build if this table drifts from what we actually ask for.
GitHub
GitHub App (installation token), not an OAuth App
- copilot:read
- organization_copilot_seat_management:read
- pull_requests:read
- contents:read
Reads: Copilot seat assignments and PR/commit metadata — counts, timestamps, review events.
Never: Source code, diffs, or file contents.
Anthropic
Admin API key, created by your org admin
- usage_report:read
- cost_report:read
- claude_code:read
Reads: Admin API — the seat roster and per-member token/usage totals.
Never: Prompts, completions, or message content.
OpenAI
Admin API key, created by your org admin
- api.usage.read
- api.costs.read
Reads: Admin API — per-member usage and cost totals.
Never: Prompts, completions, or message content.
Cursor
Admin API key, created by your org admin
- admin:members
- admin:spend
- admin:daily_usage
Reads: Admin API — the seat roster, spend, and daily usage totals.
Never: Prompts, completions, or message content.
Ramp
OAuth (authorization code), scoped at consent
- transactions:read
- bills:readoptional
Reads: Card transactions and bills, to attribute AI-vendor spend.
Never: Anything beyond what's needed to classify AI spend.
Google Workspace
OAuth service account with domain-wide delegation
- https://www.googleapis.com/auth/admin.reports.audit.readonly
Reads: Admin SDK Reports — sign-in events and OAuth grants: who signed in to which app, when.
Never: Email, files, calendar, or message content.
Okta
API token, created by your Okta admin
- okta.logs.read
Reads: System Log — sign-in events and app assignments.
Never: Credentials, MFA factors, or profile attributes beyond identity.
CSV import
No authorization: you upload the file
Reads: The rows you upload, mapped to spend, usage, or seat facts.
Never: Anything you don't upload.
Sub-processors
The short list of services we rely on
These process customer data. They are the list in §6 of the privacy policy, and we give at least 30 days' notice before adding one.
| Processor | Purpose | Region |
|---|---|---|
| Neon | Managed Postgres — the primary datastore. | United States (Mid-Atlantic, Domain 2) |
| Vercel | Application hosting and serverless compute. | United States |
| Resend | Transactional email (sign-in links, alert digests). | United States |
| PostHog | Website and product analytics. In-product events, bucketed, plus approximate location derived from IP. Never finding contents or connected data. | United States |
| Stripe | Billing and payment processing. | United States |
Website analytics
On the marketing site only, never on your data
Apollo.io
Marketing-site visitor analytics, including company-level identification of anonymous visitors. It runs only on public marketing pages, never in the app and never on your data, and we do not identify individuals. Ask us to exclude you and we will.
These run on public marketing pages, never inside the application and never against connected data, so they are not sub-processors under §6. They are disclosed in the privacy policy too. Apollo.io does not honour Do Not Track; PostHog does.
Responsible disclosure
Found something? Tell us.
If you believe you've found a security issue, email security@spendassay.com with the details and steps to reproduce. We'll acknowledge, work the fix, and keep you posted — and we won't pursue good-faith researchers who follow this process.
Run a security review against the real thing
The ungated sample report is read-only synthetic data on the same isolated infrastructure your tenant would use — the database itself keeps each customer's data apart, so one customer's query cannot reach another's rows. Point your questionnaire at it.