<!-- Markdown rendering of https://accountingkit.dev/security/ -->

> How AccountingKit protects accounting credentials and financial data, including the certifications it does not yet hold.

Trust

# Security

AccountingKit holds credentials to real accounting systems and writes entries into real books. This page describes the controls behind that, in enough detail to be checked, and states plainly what has not been independently certified.

- **Version**: 1.0
- **Effective**: 1 September 2026
- **Questions**: security@accountingkit.dev

On this page

1.  [Data you can withhold](#minimization)
2.  [Residency and architecture](#residency)
3.  [Encryption](#encryption)
4.  [Credentials and access](#credentials)
5.  [Tenant isolation](#isolation)
6.  [Integrity and recovery](#integrity)
7.  [Webhooks](#webhooks)
8.  [Logging and audit](#logging)
9.  [What we do not claim](#certifications)
10.  [Vulnerability disclosure](#disclosure)
11.  [Security reviews](#questionnaires)

## The data you can choose not to give us

The strongest control in any integration platform is the data it never holds. Every accounting connection is created in one of two retention modes, and the choice is yours.

Mode

Behaviour

Trade-off

**Zero retention**

No accounting record data is written to our database at all. Reads pass through to your provider and are returned in the response. The write path that would persist a record is not merely skipped by configuration — the request is rejected before it reaches storage

Cached list reads, background sync, and webhook-driven record projection are unavailable, because each one requires storage to exist

**Cached**

Normalized records and encrypted raw provider payloads are stored so list reads are fast, sync can run, and a failed workflow can be replayed from its last completed step. Both expire 30 days after the last sync and are removed by a scheduled clearing job

Your data rests with us for the length of that window

The mode is fixed for the life of a connection. Switching requires disconnecting and reconnecting, which is deliberate: it means the volume of your data we hold cannot change quietly through a configuration edit. Disconnecting removes the stored records for that connection.

## Residency and architecture

The API and the primary PostgreSQL database run in Google Cloud's **europe-west3 (Frankfurt, Germany)** region, and encryption keys are held in Google Cloud KMS in that same region. Your accounting data rests in the European Union.

Static assets for the marketing site and the console are served from Cloudflare's network. No accounting data is stored at the edge. Public origins are HTTPS-only with HSTS, and responses carry a strict set of security headers including frame denial, MIME-sniffing protection, a constrained permissions policy, and a content security policy.

## Encryption

-   **In transit.** TLS on every external connection, with HSTS enforced on every public origin.
-   **At rest.** Provider OAuth credentials and raw provider payloads are sealed with AES-256-GCM envelope encryption. Each record's data key is wrapped by a Google Cloud KMS key, so the ciphertext in the database cannot be read without a separate KMS authorization.
-   **Platform layer.** Database storage, backups, and disks are encrypted by the cloud platform underneath that.

## Credentials and access

-   **API keys are not recoverable.** A key is shown once, at creation. We store an HMAC-SHA256 hash under a server-side pepper, plus a prefix and the last four characters for identification. A lost key can only be revoked and replaced, never retrieved — including by us.
-   **Keys are scoped to an environment.** A test key cannot reach live data. This is enforced in the data model, not by a runtime check that could be bypassed.
-   **Provider authorization is hosted.** OAuth runs through our hosted flow, so provider credentials never pass through your application, and redirect targets are restricted to an allow-list.
-   **Runtime secrets live in a secret manager**, never in source control or an image layer, and are mounted into the service at deploy time.
-   **Console access is role-scoped** to one organization, and a key can be revoked immediately from the console.

## Tenant isolation

Every tenant-owned query proves organization ownership in SQL. Organization, environment, provider-tenant, and commercial claims supplied by a client are never trusted — ownership is re-derived server-side on each request.

The schema enforces this rather than relying on discipline. Composite foreign keys tie each connection, resource, operation, and API key back to its owning organization and environment, so a query that fails to scope correctly fails at the database rather than returning another customer's row. There is exactly one roster of users, scoped to the caller's organization, and no endpoint returns a user the caller does not share an organization with.

## Integrity and recovery

Writing to a ledger twice is a security problem as much as a correctness one. The platform is built so that a retry cannot become a duplicate entry:

-   **Idempotency is required** on mutation endpoints, and idempotency records are persisted so a repeated request returns the original result rather than performing the work again.
-   **Durable operations persist** input, step state, attempt counts, leases, and terminal errors before work is acknowledged. A run resumes from its last completed step rather than restarting.
-   **The outbox is committed with the operation**, so work is never acknowledged without being recorded, and workers claim work under database leases that expire and recover.
-   **Failure is surfaced, not hidden.** Exhausted work moves to a visible dead-letter state with its terminal error intact, so an operator can inspect and replay it.
-   **Backups.** Managed PostgreSQL with automated backups and point-in-time recovery.

## Webhooks

Outbound webhooks are signed, so your endpoint can verify a delivery genuinely came from AccountingKit before acting on it — always verify before you act. Inbound provider webhooks are verified against the provider's signing key, and their raw bodies are never logged. Delivery attempts are recorded, retried, and visible. The [documentation](https://docs.accountingkit.dev) covers verification.

## Logging and audit

Audit events record the actor, the action, the target, and a request identifier for security-relevant changes, giving both you and us an accountable history. Usage events are immutable and deduplicated, and each billing period is frozen into a statement that cannot be rewritten after the fact.

Secrets, access and refresh tokens, authorization codes, raw webhook bodies, and API keys are never written to logs. This is a standing rule in the codebase, not a filter applied afterwards.

## What we do not claim

**AccountingKit does not hold a SOC 2 report or ISO 27001 certification today.** We would rather say that plainly than imply an audit that has not happened. There is no trust-badge row on this site for the same reason.

What exists instead is described above and is verifiable: EU data residency, KMS-backed envelope encryption, unrecoverable API keys, database-enforced tenant isolation, and a zero-retention mode that removes the question of stored data entirely. For a security review, that combination is usually more useful than a certificate, because each item can be tested against the running service.

If a certification is a hard requirement for your organization, tell us at [security@accountingkit.dev](mailto:security@accountingkit.dev) — knowing which one, and by when, is what makes it worth scheduling.

## Vulnerability disclosure

We welcome good-faith security research and will not pursue action against research that follows these rules.

-   Report to [security@accountingkit.dev](mailto:security@accountingkit.dev) with steps to reproduce, the impact you believe it has, and anything we need to verify it.
-   Test only against your own test environment and provider organizations you own. Do not access another customer's data, and stop as soon as you have confirmed a finding.
-   No automated scanning of production, no denial of service, no social engineering of our personnel or our subprocessors.
-   Give us reasonable time to fix the issue before disclosing publicly. We will keep you updated while we work on it.

We acknowledge reports within 2 business days. We do not currently run a paid bounty programme, and we will say so up front rather than leave it ambiguous. Machine-readable contact details are published at [/.well-known/security.txt](https://accountingkit.dev/.well-known/security.txt).

## Security reviews

Send security questionnaires, vendor assessments, and requests for a signed [Data Processing Addendum](https://accountingkit.dev/dpa/) to [security@accountingkit.dev](mailto:security@accountingkit.dev). The [security measures annex](https://accountingkit.dev/dpa/#security) in the DPA answers most standard questionnaires directly, and the [subprocessor list](https://accountingkit.dev/subprocessors/) answers the rest of the supply-chain section.

Related

[**Terms of Service** The contract that governs access to the API, the console, and workflow execution.](https://accountingkit.dev/terms/) [**Privacy Policy** What personal data AccountingKit handles, why, where it lives, and for how long.](https://accountingkit.dev/privacy/) [**Data Processing Addendum** Article 28 processor terms, the processing record, security measures, and transfer safeguards.](https://accountingkit.dev/dpa/) [**Subprocessors** Every third party in the processing chain, what it touches, and where it runs.](https://accountingkit.dev/subprocessors/) [**Acceptable Use Policy** What you may not run through AccountingKit, and what happens if you do.](https://accountingkit.dev/acceptable-use/) [**Cookie Notice** The marketing site sets no cookies. The console sets one, and it is essential.](https://accountingkit.dev/cookies/)
