QuotaGuard and Salesforce Integration Guide

QuotaGuard Static IPs give your AppExchange ISV integration two fixed addresses to register in your External Client App’s Refresh Token IP Allowlist. Route your refresh token redemptions through QuotaGuard Static, set QUOTAGUARDSTATIC_URL, and add both IPs to the allowlist in External Client Apps Manager so redemptions keep working from cloud infrastructure.

Scope: This applies to OAuth web server flows that issue refresh tokens. JWT Bearer flow and public mobile apps are exempt. See the audience section below.

Salesforce’s Refresh Token IP Allowlist Blocks Rotating Cloud IPs

Salesforce’s May 11, 2026 OAuth security update added a Refresh Token IP Allowlist for AppExchange ISVs. When your integration calls /services/oauth2/token to redeem a refresh token, Salesforce checks the source IP against the allowlist registered for your External Client App. If the source IP is not on the allowlist, the redemption is a hard block. A cloud platform like Heroku, Render, AWS Lambda, or Fly.io assigns a rotating outbound IP, so the redemption fails the moment the allowlist is enforced. The fix is two static IPs you register once.

This Applies to OAuth Web Server Flows That Issue Refresh Tokens

The requirement applies only to OAuth flows that issue refresh tokens, which is the authorization code flow, also called the OAuth web server flow. Two common cases are exempt:

  • JWT Bearer flow does not issue refresh tokens. The integration signs a short-lived JWT and exchanges it for an access token on every call, so there is nothing to allowlist.
  • Public mobile apps connect from arbitrary consumer IPs and are not in scope.

The audience that needs this is AppExchange ISVs running OAuth web server flows from server-side infrastructure.

Getting Started

After creating a QuotaGuard account, you are redirected to your dashboard, where you can find your proxy credentials and two static IP addresses.

Choose the right proxy region: Select the QuotaGuard region closest to where your integration runs. The region is set at sign-up. Changes after sign-up require contacting support.

QUOTAGUARDSTATIC_URL="http://username:password@<your-quotaguard-proxy-host>:9293"

Add Both IPs in External Client Apps Manager

In Salesforce Setup, open External Client Apps Manager, open your External Client App, choose Edit Settings, and turn on Enforce Refresh Token IP Allowlist. Add both QuotaGuard IP addresses. Salesforce supports up to 128 IP ranges with a maximum of 256 total addresses, and both IPv4 and IPv6. Enter a single IP by using the same address as the start and end of a range. The official mechanics are at Set IP Allowlist Ranges for Refresh Tokens.

Add both IPs. QuotaGuard load-balances across the pair, so registering only one causes redemptions to fail intermittently in ways that look random in your logs.

Route Refresh Token Redemptions Through the Proxy

Route calls to /services/oauth2/token through the proxy. Use login.salesforce.com for production and test.salesforce.com for sandbox.

Python (requests)

import os, requests

proxies = {
    "http": os.environ["QUOTAGUARDSTATIC_URL"],
    "https": os.environ["QUOTAGUARDSTATIC_URL"],
}

resp = requests.post(
    "https://login.salesforce.com/services/oauth2/token",
    data={
        "grant_type": "refresh_token",
        "client_id": os.environ["SF_CLIENT_ID"],
        "client_secret": os.environ["SF_CLIENT_SECRET"],
        "refresh_token": stored_refresh_token,
    },
    proxies=proxies,
).json()

# Persist resp["refresh_token"] immediately. Under Refresh Token Rotation the
# old refresh token is invalidated the moment the new one issues.

Node.js (undici)

import { ProxyAgent, fetch } from 'undici';

const dispatcher = new ProxyAgent(process.env.QUOTAGUARDSTATIC_URL);

const res = await fetch('https://login.salesforce.com/services/oauth2/token', {
  method: 'POST',
  dispatcher,
  headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
  body: new URLSearchParams({
    grant_type: 'refresh_token',
    client_id: process.env.SF_CLIENT_ID,
    client_secret: process.env.SF_CLIENT_SECRET,
    refresh_token: storedRefreshToken,
  }),
});
const tokens = await res.json();
// Persist tokens.refresh_token transactionally before continuing.

Java (HttpClient with proxy)

// Java disables Basic auth on HTTPS CONNECT tunnels by default. Run the JVM with:
//   -Djdk.http.auth.tunneling.disabledSchemes=""
URI proxyUri = URI.create(System.getenv("QUOTAGUARDSTATIC_URL"));
String[] creds = proxyUri.getUserInfo().split(":", 2);

HttpClient client = HttpClient.newBuilder()
    .proxy(ProxySelector.of(new InetSocketAddress(proxyUri.getHost(), proxyUri.getPort())))
    .authenticator(new Authenticator() {
        @Override
        protected PasswordAuthentication getPasswordAuthentication() {
            return new PasswordAuthentication(creds[0], creds[1].toCharArray());
        }
    })
    .build();

Salesforce ISV integrations are frequently Java. The JVM flag above is required because Java disables proxy Basic authentication over HTTPS tunnels by default. Apache HttpClient 5 is an alternative that handles proxy auth without the flag.

QuotaGuard Solves the IP Layer, Not PKCE or Token Rotation

The May 11 mandate has five controls. QuotaGuard solves one: the Refresh Token IP Allowlist. PKCE and Refresh Token Rotation are code changes in your OAuth client and token storage, the 30-day idle timeout is a Salesforce-side setting plus an optional heartbeat, and IP Monitoring is a Salesforce alerting feature. QuotaGuard handles the network layer only.

Legacy Connected Apps Need a Different Path

The Refresh Token IP Allowlist UI is documented for External Client Apps Manager. If your package is still on a legacy 1GP Connected App, that UI is not available in the same form. The QuotaGuard side is identical, but confirm the Salesforce-side path with your account team: migrate to an External Client App in a 2GP package, ship a sidecar 2GP package containing only an External Client App, or open a Salesforce support case to register IP ranges manually.

Testing Your Integration Is Using the Static IP

curl -x "$QUOTAGUARDSTATIC_URL" https://ip.quotaguard.com

Expected response:

{"ip":"<one of your two QuotaGuard static IPs>"}

The returned IP must be one of the two static IPs in your QuotaGuard dashboard, and both must be on your External Client App’s Refresh Token IP Allowlist. Run it more than once to confirm both (load-balanced).

Troubleshooting

Redemption hard-blocks with an IP error

The source IP is not on the allowlist. Confirm both QuotaGuard IPs are registered under Enforce Refresh Token IP Allowlist, and that the redemption call actually routed through the proxy.

Intermittent, random-looking redemption failures

You registered only one of the two IPs. Add both.

invalid_grant after a successful redemption

This is Refresh Token Rotation, not the IP allowlist. You did not persist the new refresh token returned on the last redemption, so you replayed an invalidated one. Persist the new token transactionally on every redemption.

407 Proxy Authentication Required

This is the QuotaGuard proxy, not Salesforce. Your QUOTAGUARDSTATIC_URL credentials are wrong. Confirm them against your dashboard.

QuotaGuard Static vs QuotaGuard Shield

Feature QuotaGuard Static QuotaGuard Shield
Protocol HTTP / HTTPS / SOCKS5 HTTPS / SOCKS5 over TLS
Customer-to-proxy hop Plaintext TLS-encrypted
HTTPS payload Tunneled end-to-end, never decrypted at the proxy Tunneled end-to-end, never decrypted at the proxy
Best for Most apps Regulated data or environments that require TLS on every hop
Starting price $19/month $29/month

The IP allowlist requirement is a network-layer fix, so Static is right for most Salesforce ISVs. Choose QuotaGuard Shield if your listing also handles regulated data such as healthcare records or payment information, or if your environment requires TLS between your app and the proxy itself.


Ready to Get Started?

Get in touch or create a free trial account.

Try QuotaGuard Now

View Salesforce Integration Features

Read: Static IP for Salesforce Connected Apps and External Client Apps

Contact Support


Ready to Get Started?

Get in touch or create a free trial account

Back to top ↑

Copyright © 2009 - 2026 QuotaGuard. All rights reserved.

Copyright © 2009 - 2026 QuotaGuard. All rights reserved.