Sub-processors
**Public list. Email-subscribable; we send 30 days' advance notice
of any addition or replacement, with a right to terminate without
penalty.**
Last updated: 2026-05-25 — activated Intuit (QuickBooks Online) and Xero as opt-in sub-processors with the OAuth INTEGRATIONS_*_ENABLED flags flipped to "true". Founder-level override of the 30-day pre-listing clock (pre-listing landed 2026-05-12; full clock would have run to 2026-06-11) given the pre-launch, no-customers-on-record posture — the clock's purpose is customer notice, and there are no customers yet to notice. DPAs remain in "Pending" status until signed; the freshness gate skips Pending rows. Once paying customers exist, any further sub-processor addition restarts the full 30-day clock with no founder override available. Earlier update 2026-05-14 — pre-listed Apple Push Notification service + Web Push push services (Mozilla autopush / Google FCM / Apple Web Push) as planned-but-not-active sub-processors per Sprint 1C.2, so the 30-day advance-notice clause in DPA §6 is honoured before the iOS / browser push-notification capability ships behind the production keys. Earlier update 2026-05-12 replaced Postmark with Resend to match shipped code in apps/api/src/lib/email.ts; Sentry-the-company is not a separable sub-processor because we self-host the Sentry software on Fly.io infrastructure; the row is a footnote on the Fly entry. Pre-listed Intuit + Xero as planned per the v5 audit-batch-1 P-F5 follow-on so the same 30-day clause is honoured ahead of the first customer connecting an accounting integration.
Muntin Ledger uses six active sub-processors today, with four planned (two for the accounting-integrations capability, two for the iOS / browser push-notification capability landing in Phase 1C). Each is named. Each active sub-processor has a Data Processing Agreement on file. Each has a clear, bounded role. The deterministic v4 architecture removed the LLM sub-processor (Anthropic) entirely; no third party processes invoice content.
The 30-day notice clause and the freshness gate
DPA §6 binds us to give customers 30 days' advance notice of any new active sub-processor. The Since column below is the date on which each vendor became (or, for Planned rows, is scheduled to become) an active processor of Customer Data. The repo's scripts/check-subprocessor-freshness.mjs gate parses this column on every CI run: for any row with DPA on file = Yes, the gate fails the build if Since is less than 30 days ago. Planned rows are intentionally skipped — they are pre-disclosed precisely so the 30-day clock has run _before_ the env-flag flips on. Backdating the Since value to bypass the gate is a documented governance breach; the value MUST match the announcement-list email that went out.
The list
| # | Provider | Role | Region | DPA on file | Since |
|---|---|---|---|---|---|
| 1 | AWS | KMS for envelope encryption keys; Object Lock WORM mirror for audit log metadata. KMS only -- no AWS compute, no S3 for customer content. | us-east-2 | Yes | 2026-01-01 |
| 2 | Cloudflare | Edge network, Workers, D1 (SQLite at edge), KV, R2 (object storage), Email Routing | Global edge, US-pinned for Customer Data | Yes | 2026-01-01 |
| 3 | Fly.io | Compute for services/docling (CPU-only) and services/extract (deterministic engine; no LLM). Footnote: we self-host the Sentry error-tracking software on this same Fly tenant; no Sentry-the-company processing occurs (PII is scrubbed by tools/pii-scrubber before any error is persisted on our Fly instance). Footnote: we likewise self-host Plausible Community Edition on this same Fly tenant for cookieless, marketing-site-only analytics (ledger.muntin.digital); Plausible-the-company is not a separable sub-processor (no data leaves our Fly instance), exactly like the self-hosted Sentry above. Plausible stores no IP/UA and sets no cookies — it derives only a daily-salted, then-discarded visitor hash; the beacon is first-party (/assets/p.js + the /api/event proxy) and never loads inside the authenticated product. |
iad (US-East) | Yes | 2026-01-01 |
| 4 | Neon | Managed Postgres for extractions + extraction_templates + audit_events; row-level security enforced per org | us-east-2 | Yes | 2026-01-01 |
| 5 | Stripe | Subscription billing, payment processing | US | Yes | 2026-01-01 |
| 6 | Resend | Magic-link and account email delivery | US | Yes | 2026-04-01 |
| 7 | Intuit (QuickBooks Online) — _opt-in, processes only the customer data the operator chooses to post_ | Post bills + expenses + vendor credits to a customer's QBO realm via OAuth2. Fires only when (a) the customer explicitly connects QBO in Settings, and (b) INTEGRATIONS_QBO_ENABLED=true is set on the Worker. Both true as of 2026-05-25. DPA in negotiation; no customer data has flowed yet because no customers have connected. |
US | Pending | 2026-05-25 (activated; DPA pending signature) |
| 8 | Xero — _opt-in, processes only the customer data the operator chooses to post_ | Post bills + expenses to a Xero tenant via OAuth2. Same opt-in + env-flag gate as Intuit (#7). Both true as of 2026-05-25. DPA in negotiation; no customer data has flowed yet because no customers have connected. | US | Pending | 2026-05-25 (activated; DPA pending signature) |
| 9 | Apple Push Notification service (APNs) — _planned, opt-in, not yet processing customer data_ | Deliver push notifications to iOS devices the customer enrols at /v1/push/devices. Apple receives the generic title + body strings drawn from the 1B.10 fixed-template set (no vendor name, no amount, no email; the privacy-CI gate payloadContainsForbiddenPii enforces this at every dispatch) plus an opaque trigger_id the device dereferences locally over HTTPS after the user authenticates. Apple does not see the dereferenced detail. Activates when APNS_TEAM_ID + APNS_KEY_ID + APNS_PRIVATE_KEY_P8_BASE64 + APNS_TOPIC are all set on the Worker. |
US | Pending | Planned — 30-day notice required before enabling |
| 10 | Web Push push services (Mozilla autopush, Google FCM-as-WebPush, Apple Web Push) — _planned, opt-in, not yet processing customer data_ | Deliver push notifications to browsers (Firefox, Chrome/Chromium, Safari) the customer enrols at /v1/push/devices. Per RFC 8291, the payload is end-to-end encrypted with aes128gcm between Muntin's Worker and the browser's Service Worker; the push service forwards the ciphertext unchanged. The push service sees a per-device opaque endpoint URL, a TTL header, and the urgency hint (high for urgent category, normal otherwise). Which of the three push services reaches each browser is determined by the browser, not by Muntin — Firefox routes to Mozilla autopush, Chrome to FCM, Safari to Apple Web Push. Activates when VAPID_PRIVATE_KEY_P8_BASE64 + VAPID_PUBLIC_KEY_RAW_BASE64URL + VAPID_SUBJECT are all set on the Worker. |
US | Pending | Planned — 30-day notice required before enabling |
WorkOS (auth/SSO) is not in this list at GA. It returns when an enterprise customer demands SSO/SAML; until then there is no fourth identity-provider hop.
What each sub-processor sees
| Provider | Customer Data exposure |
|---|---|
| AWS | Ciphertext only (KMS holds keys; Object Lock holds audit-log metadata). Never plaintext invoice content. |
| Cloudflare | Ciphertext invoice files in R2; encrypted records in D1; magic-link tokens in KV (TTL 15 minutes). Worker code processes plaintext in memory only during the request lifecycle. |
| Fly.io | Plaintext invoice files in worker memory during deterministic extraction. Filesystem and process memory are scrubbed at job end. No long-term storage on Fly. No LLM call leaves Fly. |
| Neon | Encrypted records (json_blob_kms_wrapped -- DBAs cannot read records without HSM access). Per-tenant DEKs; row-level security on every table holding Customer Data. |
| Stripe | Customer billing details (name, email, payment method). Never invoice content. |
| Resend | Customer email address; magic-link verification URL. Never invoice content. |
| Intuit (QBO) — _planned_ | When (and only when) a customer connects QBO: vendor names + invoice totals + bill line items posted to their QBO realm. The customer's OAuth tokens are KMS-wrapped at rest. No data flows until the customer connects AND the env-flag is enabled. |
| Xero — _planned_ | Same shape as Intuit/QBO. Customer connects, env-flag is enabled, then bills + expenses post to their Xero tenant. KMS-wrapped tokens at rest. |
| Apple Push Notification service (APNs) — _planned_ | Generic push title + body strings drawn from a fixed template set (no vendor name, no amount, no email; the payloadContainsForbiddenPii regression guard fires before every dispatch) plus an opaque trigger_id the iOS device dereferences locally over HTTPS after the user authenticates. Apple does not see the dereferenced detail. The device token (the per-install APNs token) is stored on Muntin servers as the addressing handle; never logged in plaintext. |
| Web Push push services (Mozilla autopush, Google FCM, Apple Web Push) — _planned_ | RFC 8291 aes128gcm ciphertext only. The push service forwards the encrypted bytes to the browser's Service Worker; only the user's browser holds the key material to decrypt. The push service additionally sees a per-device opaque endpoint URL, a TTL header, and the urgency hint (high for urgent category, normal otherwise). The push service which one of the three reaches each browser is determined by the browser, not by Muntin. |
How to subscribe to changes
Email subprocessors@muntin.digital with subject subscribe. We add you to the announcement list and send 30 days' notice of any change. You can unsubscribe with reply unsubscribe at any time.
This page is the public source of truth. The version visible above matches the version in our customers' DPAs.