Security Overview

LeadRouter is built to handle sensitive sales data and Salesforce integrations securely. The headline: we don't connect AI tools directly to your CRM. We architect an air gap. The rest of this page is the details.

Architecture, not promises

Secure by design

LeadRouter does not give AI agents direct access to your Salesforce instance. Instead, it pulls a limited, defined set of fields from Salesforce into its own data layer. AI chatbots, Slack agents, and the public API only ever operate on that limited layer — never on your full CRM.

This is a structural control, not a policy. Even a worst-case prompt injection cannot reach customer records that LeadRouter never had. Your security team can verify the boundary by reading the schema, not by trusting our intentions.

The trade-off is real: Salesforce-native tools live entirely inside Salesforce, which is why they struggle to safely add AI chatbots or Slack agents. We chose the architecture that makes AI safe by default.

Authentication & Access Control

Password security

All passwords are hashed using bcrypt with a high work factor. Plain-text passwords are never written to disk or logs. Password hashes cannot be reversed.

Session tokens

Short-lived signed JWTs with 1-hour expiry, automatically refreshed in the background. Total session length is 7 days before a full re-login is required. Refresh tokens are rotated on each use and stored server-side — if a used token is replayed outside a 30-second grace window, all sessions for that user are revoked immediately and the user is notified by email. The grace window exists because browser tabs running in parallel naturally race on background refreshes; reuse within seconds of a legitimate rotation is treated as a race, not theft. Concurrent refresh requests for the same session also share a single in-flight result so they never race in the first place.

Brute-force protection

Login attempts are rate-limited per IP address. After a defined number of failed attempts within a 15-minute window, further attempts are blocked. Password reset requests are similarly rate-limited.

Password reset

Reset links are single-use, expire after 1 hour, and are invalidated immediately upon use. The system never reveals whether an email exists, preventing user enumeration.

Role-based access

Strict role separation. Tenant users can only access data belonging to their own organisation. All API routes verify both authentication and tenant scope on every request.

Two-factor authentication

TOTP-based 2FA for all user accounts. Compatible with Google Authenticator, Authy, 1Password, and any standard authenticator app. Setup provides 8 single-use recovery codes. The dashboard shows a prominent persistent prompt to enable 2FA until it's turned on — easy to ignore once, hard to forget about.

Salesforce Integration

OAuth 2.0 with PKCE

LeadRouter connects to Salesforce exclusively via OAuth 2.0 Authorization Code flow with PKCE. No Salesforce passwords are ever entered into or stored by LeadRouter.

No password storage

LeadRouter never sees or stores your Salesforce password. Access is granted via OAuth tokens that can be revoked from within Salesforce at any time, immediately cutting off access.

Encrypted token storage

All OAuth tokens — both access tokens and refresh tokens — are encrypted at rest using AES-256-CBC. They are decrypted only in memory at the moment they are needed. This is the same standard used by major integration platforms like Zapier and Make.

Refresh token rotation

Salesforce refresh tokens are rotated on each use — the old token is invalidated and a new one is issued. If a refresh token is ever intercepted, it becomes useless after the next scheduled refresh. The new rotated token is persisted on every refresh, and concurrent refresh requests for the same Salesforce connection are deduped to a single in-flight call — both deliberate, because Salesforce treats any reuse of a rotated refresh token as theft and freezes the integration user.

Connection circuit breaker

If a Salesforce token refresh fails (revocation, password reset, IP-policy block), the connection is marked broken and all further calls short-circuit immediately rather than retrying with bad credentials. The Settings page surfaces this state with a clear "Reconnect required" prompt and a one-click OAuth re-authorization. This prevents a runaway pattern of failed refresh attempts that Salesforce would interpret as an attack.

Minimal scopes

Only API access to read and update records, plus the ability to refresh sessions. No administrative or organisation-wide permissions.

Server-side API restrictions

Hardcoded allowlists restrict which Salesforce objects and fields the backend can access. Queries are limited to Lead, Contact, Account, Group, QueueSobject, and User objects. Write operations are restricted to the OwnerId field on Lead and Contact only. These restrictions are enforced at the code level before any API call is made — regardless of what the connected Salesforce user's permissions allow.

Enrollment-SOQL guardrails

Saved enrollment queries are validated server-side: a non-empty WHERE clause is required, and obvious no-op filters (WHERE Id != null, WHERE 1=1) are rejected. Lead queries automatically have IsConverted = false injected — converted leads are read-only in Salesforce, so attempts to update their owner would fail. The injection is silent and idempotent.

Baseline snapshots on first activation

When you first turn the router on, change your enrollment criteria, or re-enable after pausing, all currently-matching records are captured as "already handled" and not retroactively re-assigned. Routing only acts on records that transition into the criteria after activation. This prevents a tenant accidentally overwriting the OwnerId on every existing MQL the moment they enable LeadRouter.

Dedicated integration user

We recommend connecting with a dedicated Salesforce user that has a minimal Permission Set — read access on the objects LeadRouter needs, edit access only on OwnerId. This adds a Salesforce-enforced layer of protection: even if LeadRouter's token is compromised, the attacker is limited to what that locked-down user can do. See the in-app help for a step-by-step setup guide.

Token revocation

Disconnecting immediately deletes all stored tokens. The OAuth grant can also be revoked from within Salesforce under Connected Apps.

AI & API Security

Air-gapped data layer

AI agents, Slack integrations, and API consumers operate exclusively on the LeadRouter data layer — never on Salesforce directly. The set of fields LeadRouter pulls from Salesforce is explicit and configurable.

Per-tenant API keys

API keys are scoped to a single tenant, bcrypt-hashed at rest, and rate-limited to 100 requests per minute. Every request is logged with method, path, status, IP, and timestamp.

Webhook signing & SSRF protection

Outbound webhooks are signed with HMAC-SHA256 using a per-webhook secret. URLs are validated before delivery: HTTPS only, no localhost, no .local hostnames, no IP literals in private ranges (10.x, 172.16-31.x, 192.168.x, link-local, loopback), and no Google Cloud metadata endpoints. The IP-literal check is explicit — DNS-based validation alone would let direct IP URLs slip through. Failed deliveries are retried once after 5 seconds; outbound is fire-and-forget so it never blocks routing.

No prompt-time CRM access

AI agents cannot issue ad-hoc SOQL or pull arbitrary Salesforce data on demand. They can only see what LeadRouter has already imported through its defined schema.

Data Security

Encryption at rest

Sensitive credentials are encrypted using AES-256-CBC with a unique initialisation vector per value. Encryption keys are derived from environment secrets never stored in the codebase.

Multi-tenant data isolation

Every database record is scoped to a tenant ID. All queries are filtered by the authenticated tenant — it is architecturally impossible for one tenant to access another's data.

Data minimisation

Only stores what is required: Salesforce record IDs, assignment history, routing configuration, and rep information. No email content or lead scoring data is persisted.

Parameterised queries

All database queries use parameterised statements, preventing SQL injection attacks entirely.

Infrastructure & Network

HTTPS everywhere

All traffic is encrypted in transit via TLS. HTTP connections are not accepted.

Security headers

Standard HTTP security headers on all responses, including protections against clickjacking, MIME-type sniffing, and cross-site scripting.

CORS policy

Cross-Origin Resource Sharing is restricted to the LeadRouter frontend domain only.

Cloud infrastructure

Backend on Railway, frontend on Vercel, database on Supabase PostgreSQL. All providers maintain SOC 2 and ISO 27001 certifications.

Operational Security

Immutable audit trail

Every assignment is logged with timestamp, record ID, rep assigned, rule used, and previous owner. There is no UI to clear logs — both because the audit trail matters and because the routing engine uses these rows for deduplication, so wiping them would silently re-route everything on the next cycle. If table size becomes an issue we add a server-side prune by age, not a user-clickable wipe.

No silent drops

Every record fetched by the enrollment query ends up in either the routing log (success) or the failed-records page (failure with the specific reason). Records that don't match any rule and have no fallback are reported as configuration errors rather than dropped silently. This invariant means a tenant can always answer "where did that record go?"

Per-record retry cap

A record that fails to route is auto-retried each cycle up to a hard cap (10 attempts) before being marked "given up" and skipped. Without the cap, a permanent error (validation rule, locked record) would consume one Salesforce API call per cycle forever. Manual retry from the failed-records page resets the count.

Notification cooldowns

Routing-error emails are deduped per record and batched into a single 5-minute summary digest per tenant — a worst-case 50,000-record fan-out where every record fails produces one email per window with one row, not 50,000 individual messages. Connection-failure emails are capped at one per 24 hours per tenant. Both prevent inbox flooding without losing the signal.

Dependency management

Dependencies are kept up to date and audited for known vulnerabilities. Unused packages are removed.

Account creation

New tenants can self-register a 14-day trial via the public signup page. Within a tenant, additional users are invite-only — added by an existing admin via a one-time invite link that expires after 7 days. No password is shared in the invite; the user sets their own via the link.

Last updated: April 2026