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.

20 / 34
controls met and code-enforced, as of 26 July 2026
Read-only
the audit writes to no system you connect
Metadata
never your code, diffs, or content

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 · Met

A 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 · Met

Access 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 · Met

Read-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 · Met

Assurance

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.

ProcessorPurposeRegion
NeonManaged Postgres — the primary datastore.United States (Mid-Atlantic, Domain 2)
VercelApplication hosting and serverless compute.United States
ResendTransactional email (sign-in links, alert digests).United States
PostHogWebsite and product analytics. In-product events, bucketed, plus approximate location derived from IP. Never finding contents or connected data.United States
StripeBilling 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.