Security & data isolation

One database per client. A test for every promise.

This page lists what is true of the system and how each thing is held. It does not list certifications, because we do not hold any yet, and it does not quote an uptime figure, because we have not published one.

Each client's data lives in its own database.

Not a client column with a filter in every query. A separate database, so a bug in one query cannot show one client another client’s leads, replies or mailboxes.

Shared state is limited to what must be shared: user accounts, the plan catalogue and billing records.

How it is held: A separate database is created for each workspace at sign-up and migrated on first connect. Recorded as an architecture decision, with the pool model revisited at a stated tenant count.

Nothing sends without a person.

Scheduled jobs are read-only syncs. One may pause a losing variant. The only automated send is the delivery of a reply a person approved, at the time they chose.

Follow-on lanes derived from a campaign a person launched (an out-of-office resume, a referral copy) run inside that launch and only while five guards hold, one of which is that the workspace has not opted out.

How it is held: A source-level test in the build fails if any scheduled job references a send entry point. It is a scan, not full transitive analysis, and the test's own comments say so.

Every authorisation decision goes through one place.

Roles are owner, member and viewer within a workspace, and an operator role that is never granted by sign-up. A workspace owner manages their own team, integrations and credentials; secrets are write-only once stored.

How it is held: A single authorisation class; a test that fails the build if an admin route is matched after the tenant catch-all, which is the silent privilege-escalation mistake; a test that fails it if the owner tenant's name is hardcoded.

Sign-up is throttled and verified.

An unverified workspace can be explored but cannot verify leads or spend credits. The resend endpoint answers the same way whether or not the address exists, so it cannot be used to enumerate accounts.

How it is held: A per-address and per-email throttle on sign-up and resend; a honeypot field; a verification token with an expiry. The sign-up response reports whether the verification email was actually sent, never assumes it.

Every action is attributed.

Approvals, edits, launches, pauses, purchases, invitations. When a verification email could not be sent, the log says so rather than pretending it went.

How it is held: An activity log per workspace, readable by its owner, that records the action, who took it and when, including the ones that failed.

A number we do not have is never a zero.

It is a security property as much as an honesty one: a fabricated healthy figure is how an estate burns quietly while the screen stays green.

How it is held: Recorded as an architecture decision. Nullable types end to end, so 'not set' is distinct from 0 in the database, the API and the screen; rates are not computed from a partial pair.

Vendor-first, then local.

When a change involves an upstream provider, the provider is updated first and the local record second, so an upstream failure surfaces as an error and the workspace never believes something that did not happen.

How it is held: Recorded as an architecture decision, with a test that maps every vendor failure to a 502 rather than a 500.
Stated plainly

What we do not claim.

  • No SOC 2, ISO 27001 or similar certification is held or in progress that we can cite.
  • No uptime figure is published.
  • No penetration test report is available on request yet.
  • Data residency is not selectable per workspace.
  • A data processing agreement is not yet published. If you need one to proceed, ask and we will tell you honestly where it stands.

When any of these changes, this page will change with it. Until then, absence here means absence.

Your own database, from the first minute.

Every workspace is created with one.