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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
Only API access to read and update records, plus the ability to refresh sessions. No administrative or organisation-wide permissions.
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.
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.
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.
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.
Disconnecting immediately deletes all stored tokens. The OAuth grant can also be revoked from within Salesforce under Connected Apps.
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.
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.
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.
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.
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.
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.
Only stores what is required: Salesforce record IDs, assignment history, routing configuration, and rep information. No email content or lead scoring data is persisted.
All database queries use parameterised statements, preventing SQL injection attacks entirely.
All traffic is encrypted in transit via TLS. HTTP connections are not accepted.
Standard HTTP security headers on all responses, including protections against clickjacking, MIME-type sniffing, and cross-site scripting.
Cross-Origin Resource Sharing is restricted to the LeadRouter frontend domain only.
Backend on Railway, frontend on Vercel, database on Supabase PostgreSQL. All providers maintain SOC 2 and ISO 27001 certifications.
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.
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?"
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.
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.
Dependencies are kept up to date and audited for known vulnerabilities. Unused packages are removed.
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