AccountingKitDocs

Security

Credential protection, tenant isolation, and webhook trust.

Secrets and credentials

API key secrets are returned once, at creation, and stored only as peppered HMAC digests. A lost key can be revoked and replaced, never retrieved — including by us. Your Xero app credentials, provider OAuth tokens, webhook signing secrets, and raw provider payloads are sealed with AES-256-GCM envelope encryption, and each data key is wrapped by a Google Cloud KMS key held in the same region as the database.

Bearer keys, OAuth tokens, raw accounting payloads, signing secrets, authorization codes, and document contents are never written to logs. That is a standing rule in the codebase, not a filter applied afterwards.

Internal service-to-service calls carry their own signed identity and a separate shared credential. Neither is ever an API key pepper, a user credential, or a provider secret.

Tenant isolation

Every query that touches product data proves organization and environment ownership in SQL, and the schema enforces it: composite foreign keys tie each connection, resource, operation, and API key back to its owning organization and environment. A query that fails to scope correctly fails at the database rather than returning another tenant's row.

The environment is selected by the API key, never by request input, so a test key cannot reach live data. Console mutations additionally require an authenticated organization membership and an explicit accounting permission.

OAuth

Connect sessions are short-lived, single-use, and carry high-entropy state. Their server-side record binds organization, environment, Xero app, customer identifier, and retention mode. Redirect URIs are checked against the configured allowlist. Connections with ambiguous Xero tenants remain pending until the customer selects one.

Webhooks

Inbound Xero and outbound customer signatures are verified against raw bytes with constant-time comparison. Event IDs are persisted for deduplication. Customer destinations must use public HTTPS on port 443. Delivery resolves and validates every address, rejects private and reserved ranges, and pins the validated address for the request to prevent DNS rebinding.

Retention and auditability

Cached normalized data contains no raw provider payload; encrypted raw provider payloads expire after 30 days. A zero-retention connection persists neither representation and supports live reads only. Disconnecting clears tokens, provider identifiers, cached accounting data, and payload-bearing operational records. Idempotency records expire after 30 days. Audit events, redacted operation state, and immutable usage events remain available for incident reconstruction.

The production deployment runs least-privilege service accounts, private worker ingress, managed PostgreSQL backups with point-in-time recovery, KMS key rotation, and centralized log retention.

Reviews and disclosure

Data residency, the current subprocessor list, the Data Processing Addendum, and our vulnerability disclosure policy are published at accountingkit.dev/security.

On this page