Integrating RequestRocket into Your SaaS
Learn how to use RequestRocket to power integrations in your SaaS application
Integrating RequestRocket into Your SaaS
Learn how to use RequestRocket as the integration layer for your SaaS application, enabling your customers to connect with third-party platforms securely and reliably.
Why Use RequestRocket for Your SaaS?
RequestRocket simplifies complex integration scenarios by:
- Managing Authentication: Handle complex OAuth, API keys, and custom auth for each customer
- Isolating Customer Data: Each customer gets their own credentials and proxies
- Centralizing Management: Programmatically manage all customer integrations via the Core API
- Ensuring Security: Credentials are encrypted and never exposed to your application
- Enabling Self-Service: Give customers API keys to manage their own integrations
Integration Patterns
Pattern 1: Managed Integrations
Your SaaS manages all integrations for customers, storing their third-party credentials securely.
Use Cases:
- Integration marketplace
- Pre-built connectors
- Managed data sync
Pattern 2: Customer Self-Service
Customers use API keys you provide to make their own requests through RequestRocket.
Use Cases:
- Developer platforms
- API-as-a-Service
- White-label solutions
Pattern 3: Multi-Tenant Isolation
Create separate RequestRocket clients for each of your customers.
Use Cases:
- Enterprise customers
- Compliance requirements
- Complete isolation
Implementation Guide
Set Up Your RequestRocket Infrastructure
Create your main SaaS client:
# Create your main SaaS client
curl -X POST "https://api.requestrocket.com/clients" \
-H "Authorization: $REQUESTROCKET_USER_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"clientName": "MySaaS Integration Manager"
}'
# Response will include:
# {
# "clientId": "cli_abc123...",
# "clientName": "MySaaS Integration Manager",
# ...
# }// Initialize your SaaS's RequestRocket client
const RR_USER_TOKEN = process.env.REQUESTROCKET_USER_TOKEN;
async function initializeSaaS() {
// Create your main client
const response = await fetch('https://api.requestrocket.com/clients', {
method: 'POST',
headers: {
'Authorization': RR_USER_TOKEN,
'Content-Type': 'application/json'
},
body: JSON.stringify({
clientName: 'MySaaS Integration Manager'
})
});
const client = await response.json();
console.log('SaaS Client ID:', client.clientId);
return client;
}import requests
import os
RR_USER_TOKEN = os.getenv('REQUESTROCKET_USER_TOKEN')
def initialize_saas():
"""Initialize your SaaS's RequestRocket client"""
response = requests.post(
'https://api.requestrocket.com/clients',
headers={
'Authorization': RR_USER_TOKEN,
'Content-Type': 'application/json'
},
json={
'clientName': 'MySaaS Integration Manager'
}
)
client = response.json()
print(f'SaaS Client ID: {client["clientId"]}')
return clientpackage main
import (
"bytes"
"encoding/json"
"fmt"
"io"
"net/http"
"os"
)
type Client struct {
ClientID string `json:"clientId"`
ClientName string `json:"clientName"`
}
func initializeSaaS() (*Client, error) {
rrToken := os.Getenv("REQUESTROCKET_USER_TOKEN")
payload := map[string]string{
"clientName": "MySaaS Integration Manager",
}
jsonData, err := json.Marshal(payload)
if err != nil {
return nil, err
}
req, err := http.NewRequest(
"POST",
"https://api.requestrocket.com/clients",
bytes.NewBuffer(jsonData),
)
if err != nil {
return nil, err
}
req.Header.Set("Authorization", rrToken)
req.Header.Set("Content-Type", "application/json")
client := &http.Client{}
resp, err := client.Do(req)
if err != nil {
return nil, err
}
defer resp.Body.Close()
body, err := io.ReadAll(resp.Body)
if err != nil {
return nil, err
}
var result Client
if err := json.Unmarshal(body, &result); err != nil {
return nil, err
}
fmt.Printf("SaaS Client ID: %s\n", result.ClientID)
return &result, nil
}import okhttp3.*;
import com.google.gson.Gson;
import java.io.IOException;
public class SaaSInitializer {
private static final String RR_USER_TOKEN = System.getenv("REQUESTROCKET_USER_TOKEN");
private static final OkHttpClient httpClient = new OkHttpClient();
private static final Gson gson = new Gson();
public static class Client {
public String clientId;
public String clientName;
}
public static Client initializeSaaS() throws IOException {
// Create request payload
String json = gson.toJson(new Object() {
final String clientName = "MySaaS Integration Manager";
});
RequestBody body = RequestBody.create(
json,
MediaType.parse("application/json")
);
Request request = new Request.Builder()
.url("https://api.requestrocket.com/clients")
.header("Authorization", RR_USER_TOKEN)
.header("Content-Type", "application/json")
.post(body)
.build();
try (Response response = httpClient.newCall(request).execute()) {
if (!response.isSuccessful()) {
throw new IOException("Request failed: " + response);
}
Client client = gson.fromJson(
response.body().string(),
Client.class
);
System.out.println("SaaS Client ID: " + client.clientId);
return client;
}
}
}Create Integration for a New Customer
When a customer connects a third-party service:
# 1. Create proxy credential for your customer
curl -X POST "https://api.requestrocket.com/clients/$SAAS_CLIENT_ID/credentials" \
-H "Authorization: $RR_USER_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"credentialName": "customer123-proxy-salesforce",
"credentialType": "proxy",
"authType": "apiKey",
"region": "us-east-1",
"secret": {
"keyName": "X-API-Key",
"keyValue": "generated-secure-key"
}
}'
# 2. Create target credential (customer's third-party creds)
curl -X POST "https://api.requestrocket.com/clients/$SAAS_CLIENT_ID/credentials" \
-H "Authorization: $RR_USER_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"credentialName": "customer123-salesforce-target",
"credentialType": "target",
"authType": "oauth2",
"region": "us-east-1",
"secret": {
"accessToken": "customer-oauth-token",
"refreshToken": "customer-refresh-token"
}
}'
# 3. Get or create target
curl -X GET "https://api.requestrocket.com/clients/$SAAS_CLIENT_ID/targets?targetName=Salesforce" \
-H "Authorization: $RR_USER_TOKEN"
# 4. Create proxy
curl -X POST "https://api.requestrocket.com/clients/$SAAS_CLIENT_ID/proxies" \
-H "Authorization: $RR_USER_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"proxyName": "customer123-salesforce",
"region": "us-east-1",
"proxyCredentialId": "crd_proxy123",
"targetId": "tgt_sf123",
"targetCredentialId": "crd_target123",
"active": true
}'
# Store the returned proxy details in your databaseasync function createCustomerIntegration(customerId, integration) {
const { platform, credentials } = integration;
// 1. Create proxy credential for your customer
const proxyCred = await fetch(
`https://api.requestrocket.com/clients/${SAAS_CLIENT_ID}/credentials`,
{
method: 'POST',
headers: {
'Authorization': RR_USER_TOKEN,
'Content-Type': 'application/json'
},
body: JSON.stringify({
credentialName: `${customerId}-proxy-${platform}`,
credentialType: 'proxy',
authType: 'apiKey',
region: 'us-east-1',
secret: {
keyName: 'X-API-Key',
keyValue: generateSecureKey() // Generate unique key for customer
}
})
}
).then(r => r.json());
// 2. Create target credential (customer's third-party creds)
const targetCred = await fetch(
`https://api.requestrocket.com/clients/${SAAS_CLIENT_ID}/credentials`,
{
method: 'POST',
headers: {
'Authorization': RR_USER_TOKEN,
'Content-Type': 'application/json'
},
body: JSON.stringify({
credentialName: `${customerId}-${platform}-target`,
credentialType: 'target',
authType: credentials.type, // e.g., 'oauth2', 'apiKey'
region: 'us-east-1',
secret: credentials.secret
})
}
).then(r => r.json());
// 3. Create or get target
const target = await ensureTarget(platform);
// 4. Create proxy
const proxy = await fetch(
`https://api.requestrocket.com/clients/${SAAS_CLIENT_ID}/proxies`,
{
method: 'POST',
headers: {
'Authorization': RR_USER_TOKEN,
'Content-Type': 'application/json'
},
body: JSON.stringify({
proxyName: `${customerId}-${platform}`,
region: 'us-east-1',
proxyCredentialId: proxyCred.credentialId,
targetId: target.targetId,
targetCredentialId: targetCred.credentialId,
active: true
})
}
).then(r => r.json());
// 5. Store mapping in your database
await db.integrations.create({
customerId,
platform,
proxyId: proxy.proxyId,
proxyCredentialId: proxyCred.credentialId,
proxyUrl: proxy.baseURL,
customerApiKey: proxyCred.secret.keyValue
});
return {
proxyUrl: proxy.baseURL,
apiKey: proxyCred.secret.keyValue
};
}def create_customer_integration(customer_id, integration):
"""Create integration for a new customer"""
platform = integration['platform']
credentials = integration['credentials']
# 1. Create proxy credential for your customer
proxy_cred_response = requests.post(
f'https://api.requestrocket.com/clients/{SAAS_CLIENT_ID}/credentials',
headers={
'Authorization': RR_USER_TOKEN,
'Content-Type': 'application/json'
},
json={
'credentialName': f'{customer_id}-proxy-{platform}',
'credentialType': 'proxy',
'authType': 'apiKey',
'region': 'us-east-1',
'secret': {
'keyName': 'X-API-Key',
'keyValue': generate_secure_key() # Generate unique key
}
}
)
proxy_cred = proxy_cred_response.json()
# 2. Create target credential (customer's third-party creds)
target_cred_response = requests.post(
f'https://api.requestrocket.com/clients/{SAAS_CLIENT_ID}/credentials',
headers={
'Authorization': RR_USER_TOKEN,
'Content-Type': 'application/json'
},
json={
'credentialName': f'{customer_id}-{platform}-target',
'credentialType': 'target',
'authType': credentials['type'],
'region': 'us-east-1',
'secret': credentials['secret']
}
)
target_cred = target_cred_response.json()
# 3. Create or get target
target = ensure_target(platform)
# 4. Create proxy
proxy_response = requests.post(
f'https://api.requestrocket.com/clients/{SAAS_CLIENT_ID}/proxies',
headers={
'Authorization': RR_USER_TOKEN,
'Content-Type': 'application/json'
},
json={
'proxyName': f'{customer_id}-{platform}',
'region': 'us-east-1',
'proxyCredentialId': proxy_cred['credentialId'],
'targetId': target['targetId'],
'targetCredentialId': target_cred['credentialId'],
'active': True
}
)
proxy = proxy_response.json()
# 5. Store mapping in your database
db.integrations.create({
'customerId': customer_id,
'platform': platform,
'proxyId': proxy['proxyId'],
'proxyCredentialId': proxy_cred['credentialId'],
'proxyUrl': proxy['baseURL'],
'customerApiKey': proxy_cred['secret']['keyValue']
})
return {
'proxyUrl': proxy['baseURL'],
'apiKey': proxy_cred['secret']['keyValue']
}type Integration struct {
Platform string `json:"platform"`
Credentials map[string]interface{} `json:"credentials"`
}
type IntegrationResult struct {
ProxyURL string `json:"proxyUrl"`
APIKey string `json:"apiKey"`
}
func createCustomerIntegration(customerID string, integration Integration) (*IntegrationResult, error) {
// 1. Create proxy credential
proxyCredPayload := map[string]interface{}{
"credentialName": fmt.Sprintf("%s-proxy-%s", customerID, integration.Platform),
"credentialType": "proxy",
"authType": "apiKey",
"region": "us-east-1",
"secret": map[string]string{
"keyName": "X-API-Key",
"keyValue": generateSecureKey(),
},
}
proxyCred, err := createCredential(SAAS_CLIENT_ID, proxyCredPayload)
if err != nil {
return nil, err
}
// 2. Create target credential
targetCredPayload := map[string]interface{}{
"credentialName": fmt.Sprintf("%s-%s-target", customerID, integration.Platform),
"credentialType": "target",
"authType": integration.Credentials["type"],
"region": "us-east-1",
"secret": integration.Credentials["secret"],
}
targetCred, err := createCredential(SAAS_CLIENT_ID, targetCredPayload)
if err != nil {
return nil, err
}
// 3. Create or get target
target, err := ensureTarget(integration.Platform)
if err != nil {
return nil, err
}
// 4. Create proxy
proxyPayload := map[string]interface{}{
"proxyName": fmt.Sprintf("%s-%s", customerID, integration.Platform),
"region": "us-east-1",
"proxyCredentialId": proxyCred["credentialId"],
"targetId": target["targetId"],
"targetCredentialId": targetCred["credentialId"],
"active": true,
}
proxy, err := createProxy(SAAS_CLIENT_ID, proxyPayload)
if err != nil {
return nil, err
}
// 5. Store mapping in database
err = db.Integrations.Create(map[string]interface{}{
"customerId": customerID,
"platform": integration.Platform,
"proxyId": proxy["proxyId"],
"proxyCredentialId": proxyCred["credentialId"],
"proxyUrl": proxy["baseURL"],
"customerApiKey": proxyCred["secret"].(map[string]interface{})["keyValue"],
})
if err != nil {
return nil, err
}
return &IntegrationResult{
ProxyURL: proxy["baseURL"].(string),
APIKey: proxyCred["secret"].(map[string]interface{})["keyValue"].(string),
}, nil
}public class IntegrationResult {
public String proxyUrl;
public String apiKey;
}
public IntegrationResult createCustomerIntegration(
String customerId,
Map<String, Object> integration
) throws IOException {
String platform = (String) integration.get("platform");
Map<String, Object> credentials = (Map<String, Object>) integration.get("credentials");
// 1. Create proxy credential
Map<String, Object> proxyCredPayload = new HashMap<>();
proxyCredPayload.put("credentialName", customerId + "-proxy-" + platform);
proxyCredPayload.put("credentialType", "proxy");
proxyCredPayload.put("authType", "apiKey");
proxyCredPayload.put("region", "us-east-1");
Map<String, String> proxySecret = new HashMap<>();
proxySecret.put("keyName", "X-API-Key");
proxySecret.put("keyValue", generateSecureKey());
proxyCredPayload.put("secret", proxySecret);
Map<String, Object> proxyCred = createCredential(SAAS_CLIENT_ID, proxyCredPayload);
// 2. Create target credential
Map<String, Object> targetCredPayload = new HashMap<>();
targetCredPayload.put("credentialName", customerId + "-" + platform + "-target");
targetCredPayload.put("credentialType", "target");
targetCredPayload.put("authType", credentials.get("type"));
targetCredPayload.put("region", "us-east-1");
targetCredPayload.put("secret", credentials.get("secret"));
Map<String, Object> targetCred = createCredential(SAAS_CLIENT_ID, targetCredPayload);
// 3. Create or get target
Map<String, Object> target = ensureTarget(platform);
// 4. Create proxy
Map<String, Object> proxyPayload = new HashMap<>();
proxyPayload.put("proxyName", customerId + "-" + platform);
proxyPayload.put("region", "us-east-1");
proxyPayload.put("proxyCredentialId", proxyCred.get("credentialId"));
proxyPayload.put("targetId", target.get("targetId"));
proxyPayload.put("targetCredentialId", targetCred.get("credentialId"));
proxyPayload.put("active", true);
Map<String, Object> proxy = createProxy(SAAS_CLIENT_ID, proxyPayload);
// 5. Store mapping in database
Map<String, Object> dbRecord = new HashMap<>();
dbRecord.put("customerId", customerId);
dbRecord.put("platform", platform);
dbRecord.put("proxyId", proxy.get("proxyId"));
dbRecord.put("proxyCredentialId", proxyCred.get("credentialId"));
dbRecord.put("proxyUrl", proxy.get("baseURL"));
Map<String, String> secret = (Map<String, String>) proxyCred.get("secret");
dbRecord.put("customerApiKey", secret.get("keyValue"));
db.integrations.create(dbRecord);
IntegrationResult result = new IntegrationResult();
result.proxyUrl = (String) proxy.get("baseURL");
result.apiKey = secret.get("keyValue");
return result;
}Make Requests on Behalf of Customers
When your SaaS needs to sync data or perform operations:
# Make request through customer's proxy
# Get the customer's proxy URL and API key from your database
curl -X GET "https://us-east-1.requestrocket.com/api/prx_customer123/api/customers" \
-H "Authorization: Bearer customer-proxy-api-key" \
-H "Content-Type: application/json"
# POST request example
curl -X POST "https://us-east-1.requestrocket.com/api/prx_customer123/api/orders" \
-H "Authorization: Bearer customer-proxy-api-key" \
-H "Content-Type: application/json" \
-d '{
"productId": "prod_123",
"quantity": 5
}'async function syncCustomerData(customerId, platform) {
// Get customer's integration
const integration = await db.integrations.findOne({
customerId,
platform
});
// Make request through their proxy
const response = await fetch(
`${integration.proxyUrl}/api/customers`,
{
headers: {
'Authorization': `Bearer ${integration.customerApiKey}`,
'Content-Type': 'application/json'
}
}
);
const data = await response.json();
// Process and store data
await processCustomerData(customerId, platform, data);
return data;
}def sync_customer_data(customer_id, platform):
"""Sync data for a specific customer"""
# Get customer's integration
integration = db.integrations.find_one({
'customerId': customer_id,
'platform': platform
})
# Make request through their proxy
response = requests.get(
f"{integration['proxyUrl']}/api/customers",
headers={
'Authorization': f"Bearer {integration['customerApiKey']}",
'Content-Type': 'application/json'
}
)
data = response.json()
# Process and store data
process_customer_data(customer_id, platform, data)
return datafunc syncCustomerData(customerID, platform string) (interface{}, error) {
// Get customer's integration
integration, err := db.Integrations.FindOne(map[string]interface{}{
"customerId": customerID,
"platform": platform,
})
if err != nil {
return nil, err
}
// Make request through their proxy
req, err := http.NewRequest(
"GET",
integration["proxyUrl"].(string)+"/api/customers",
nil,
)
if err != nil {
return nil, err
}
req.Header.Set("Authorization", "Bearer "+integration["customerApiKey"].(string))
req.Header.Set("Content-Type", "application/json")
client := &http.Client{}
resp, err := client.Do(req)
if err != nil {
return nil, err
}
defer resp.Body.Close()
var data interface{}
if err := json.NewDecoder(resp.Body).Decode(&data); err != nil {
return nil, err
}
// Process and store data
if err := processCustomerData(customerID, platform, data); err != nil {
return nil, err
}
return data, nil
}public Object syncCustomerData(String customerId, String platform) throws IOException {
// Get customer's integration
Map<String, Object> integration = db.integrations.findOne(
Map.of("customerId", customerId, "platform", platform)
);
// Make request through their proxy
String url = integration.get("proxyUrl") + "/api/customers";
Request request = new Request.Builder()
.url(url)
.header("Authorization", "Bearer " + integration.get("customerApiKey"))
.header("Content-Type", "application/json")
.get()
.build();
try (Response response = httpClient.newCall(request).execute()) {
if (!response.isSuccessful()) {
throw new IOException("Request failed: " + response);
}
String json = response.body().string();
Object data = gson.fromJson(json, Object.class);
// Process and store data
processCustomerData(customerId, platform, data);
return data;
}
}Enable Customer Self-Service
Let customers make their own API calls:
// Provide customers with their integration details
app.get('/api/integrations/:platform', authenticateCustomer, async (req, res) => {
const { customerId } = req.user;
const { platform } = req.params;
const integration = await db.integrations.findOne({
customerId,
platform
});
if (!integration) {
return res.status(404).json({ error: 'Integration not found' });
}
res.json({
platform,
proxyUrl: integration.proxyUrl,
apiKey: integration.customerApiKey, // Give them the key
documentation: `/docs/integrations/${platform}`
});
});Customers can then use your proxy directly:
// Customer's code
const response = await fetch(
'https://us-east-1.requestrocket.com/api/prx_abc/orders',
{
headers: {
'Authorization': 'Bearer customer-api-key-from-your-saas',
'Content-Type': 'application/json'
}
}
);Advanced Patterns
Multi-Client Architecture
For enterprise customers or compliance requirements, create separate RequestRocket clients:
# Create dedicated RequestRocket client for enterprise customer
curl -X POST "https://api.requestrocket.com/clients" \
-H "Authorization: $RR_USER_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"clientName": "Enterprise: Acme Corp"
}'
# Response includes the new clientId:
# {
# "clientId": "cli_enterprise123",
# "clientName": "Enterprise: Acme Corp",
# ...
# }
# Now create integrations under their dedicated client
# All subsequent credential/proxy creation uses their cli_enterprise123
curl -X POST "https://api.requestrocket.com/clients/cli_enterprise123/credentials" \
-H "Authorization: $RR_USER_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"credentialName": "acme-salesforce-proxy",
"credentialType": "proxy",
...
}'async function provisionEnterpriseCustomer(customerId, customerName) {
// Create dedicated RequestRocket client for this customer
const client = await fetch('https://api.requestrocket.com/clients', {
method: 'POST',
headers: {
'Authorization': RR_USER_TOKEN,
'Content-Type': 'application/json'
},
body: JSON.stringify({
clientName: `Enterprise: ${customerName}`
})
}).then(r => r.json());
// Store mapping
await db.customers.update(customerId, {
rrClientId: client.clientId,
tier: 'enterprise'
});
return client;
}
// Now create all their integrations under their own client
async function createEnterpriseIntegration(customerId, platform, credentials) {
const customer = await db.customers.findById(customerId);
const clientId = customer.rrClientId; // Their dedicated client
// Create credentials and proxies under their client
// ... (same as before but using their clientId)
}def provision_enterprise_customer(customer_id, customer_name):
"""Create dedicated RequestRocket client for enterprise customer"""
# Create dedicated client
response = requests.post(
'https://api.requestrocket.com/clients',
headers={
'Authorization': RR_USER_TOKEN,
'Content-Type': 'application/json'
},
json={
'clientName': f'Enterprise: {customer_name}'
}
)
client = response.json()
# Store mapping
db.customers.update(customer_id, {
'rrClientId': client['clientId'],
'tier': 'enterprise'
})
return client
def create_enterprise_integration(customer_id, platform, credentials):
"""Create integration under customer's dedicated client"""
customer = db.customers.find_by_id(customer_id)
client_id = customer['rrClientId'] # Their dedicated client
# Create credentials and proxies under their client
# ... (same as before but using their client_id)func provisionEnterpriseCustomer(customerID, customerName string) (*Client, error) {
// Create dedicated RequestRocket client
payload := map[string]string{
"clientName": fmt.Sprintf("Enterprise: %s", customerName),
}
jsonData, err := json.Marshal(payload)
if err != nil {
return nil, err
}
req, err := http.NewRequest(
"POST",
"https://api.requestrocket.com/clients",
bytes.NewBuffer(jsonData),
)
if err != nil {
return nil, err
}
req.Header.Set("Authorization", RR_USER_TOKEN)
req.Header.Set("Content-Type", "application/json")
resp, err := http.DefaultClient.Do(req)
if err != nil {
return nil, err
}
defer resp.Body.Close()
var client Client
if err := json.NewDecoder(resp.Body).Decode(&client); err != nil {
return nil, err
}
// Store mapping
err = db.Customers.Update(customerID, map[string]interface{}{
"rrClientId": client.ClientID,
"tier": "enterprise",
})
return &client, err
}
func createEnterpriseIntegration(customerID, platform string, credentials map[string]interface{}) error {
customer, err := db.Customers.FindByID(customerID)
if err != nil {
return err
}
clientID := customer["rrClientId"].(string) // Their dedicated client
// Create credentials and proxies under their client
// ... (same as before but using their clientID)
return nil
}public Client provisionEnterpriseCustomer(String customerId, String customerName)
throws IOException {
// Create dedicated RequestRocket client
Map<String, String> payload = new HashMap<>();
payload.put("clientName", "Enterprise: " + customerName);
String json = gson.toJson(payload);
RequestBody body = RequestBody.create(
json,
MediaType.parse("application/json")
);
Request request = new Request.Builder()
.url("https://api.requestrocket.com/clients")
.header("Authorization", RR_USER_TOKEN)
.header("Content-Type", "application/json")
.post(body)
.build();
try (Response response = httpClient.newCall(request).execute()) {
String responseJson = response.body().string();
Client client = gson.fromJson(responseJson, Client.class);
// Store mapping
Map<String, Object> updates = new HashMap<>();
updates.put("rrClientId", client.clientId);
updates.put("tier", "enterprise");
db.customers.update(customerId, updates);
return client;
}
}
public void createEnterpriseIntegration(
String customerId,
String platform,
Map<String, Object> credentials
) throws IOException {
Map<String, Object> customer = db.customers.findById(customerId);
String clientId = (String) customer.get("rrClientId"); // Their dedicated client
// Create credentials and proxies under their client
// ... (same as before but using their clientId)
}Authorization Rules per Customer
Apply different access rules for different customers:
# Apply rate limit rule for basic tier customer
curl -X POST "https://api.requestrocket.com/clients/$SAAS_CLIENT_ID/proxies/$PROXY_ID/rules" \
-H "Authorization: $RR_USER_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"effect": "deny",
"priority": 900,
"notes": "Basic tier rate limit - max 100 req/hour"
}'
# Restrict specific endpoint for customer
curl -X POST "https://api.requestrocket.com/clients/$SAAS_CLIENT_ID/proxies/$PROXY_ID/rules" \
-H "Authorization: $RR_USER_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"effect": "deny",
"priority": 800,
"pathPattern": "/admin/*",
"pathPresence": "present",
"notes": "Admin endpoints restricted for customer123"
}'
# Allow only specific HTTP methods
curl -X POST "https://api.requestrocket.com/clients/$SAAS_CLIENT_ID/proxies/$PROXY_ID/rules" \
-H "Authorization: $RR_USER_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"effect": "deny",
"priority": 850,
"methodPattern": "DELETE|PUT",
"methodPresence": "present",
"notes": "Read-only access - deny modifications"
}'async function applyCustomerRules(customerId, proxyId, ruleConfig) {
const rules = [];
// Example: Rate limit per customer tier
if (ruleConfig.tier === 'basic') {
rules.push({
effect: 'deny',
priority: 900,
notes: 'Basic tier rate limit',
// Add custom rate limiting logic
});
}
// Example: Restrict certain endpoints
if (ruleConfig.restrictedPaths) {
for (const path of ruleConfig.restrictedPaths) {
rules.push({
effect: 'deny',
priority: 800,
pathPattern: path,
pathPresence: 'present',
notes: `Restricted path for ${customerId}`
});
}
}
// Create rules
for (const rule of rules) {
await fetch(
`https://api.requestrocket.com/clients/${SAAS_CLIENT_ID}/proxies/${proxyId}/rules`,
{
method: 'POST',
headers: {
'Authorization': RR_USER_TOKEN,
'Content-Type': 'application/json'
},
body: JSON.stringify(rule)
}
);
}
}def apply_customer_rules(customer_id, proxy_id, rule_config):
"""Apply authorization rules based on customer tier and config"""
rules = []
# Example: Rate limit per customer tier
if rule_config.get('tier') == 'basic':
rules.append({
'effect': 'deny',
'priority': 900,
'notes': 'Basic tier rate limit'
})
# Example: Restrict certain endpoints
if rule_config.get('restrictedPaths'):
for path in rule_config['restrictedPaths']:
rules.append({
'effect': 'deny',
'priority': 800,
'pathPattern': path,
'pathPresence': 'present',
'notes': f'Restricted path for {customer_id}'
})
# Example: Method restrictions (read-only)
if rule_config.get('readOnly'):
rules.append({
'effect': 'deny',
'priority': 850,
'methodPattern': 'DELETE|PUT|PATCH',
'methodPresence': 'present',
'notes': f'Read-only access for {customer_id}'
})
# Create rules
for rule in rules:
response = requests.post(
f'https://api.requestrocket.com/clients/{SAAS_CLIENT_ID}/proxies/{proxy_id}/rules',
headers={
'Authorization': RR_USER_TOKEN,
'Content-Type': 'application/json'
},
json=rule
)
response.raise_for_status()func applyCustomerRules(customerID, proxyID string, ruleConfig map[string]interface{}) error {
rules := []map[string]interface{}{}
// Example: Rate limit per customer tier
if tier, ok := ruleConfig["tier"].(string); ok && tier == "basic" {
rules = append(rules, map[string]interface{}{
"effect": "deny",
"priority": 900,
"notes": "Basic tier rate limit",
})
}
// Example: Restrict certain endpoints
if restrictedPaths, ok := ruleConfig["restrictedPaths"].([]string); ok {
for _, path := range restrictedPaths {
rules = append(rules, map[string]interface{}{
"effect": "deny",
"priority": 800,
"pathPattern": path,
"pathPresence": "present",
"notes": fmt.Sprintf("Restricted path for %s", customerID),
})
}
}
// Example: Method restrictions (read-only)
if readOnly, ok := ruleConfig["readOnly"].(bool); ok && readOnly {
rules = append(rules, map[string]interface{}{
"effect": "deny",
"priority": 850,
"methodPattern": "DELETE|PUT|PATCH",
"methodPresence": "present",
"notes": fmt.Sprintf("Read-only access for %s", customerID),
})
}
// Create rules
for _, rule := range rules {
jsonData, err := json.Marshal(rule)
if err != nil {
return err
}
url := fmt.Sprintf(
"https://api.requestrocket.com/clients/%s/proxies/%s/rules",
SAAS_CLIENT_ID,
proxyID,
)
req, err := http.NewRequest("POST", url, bytes.NewBuffer(jsonData))
if err != nil {
return err
}
req.Header.Set("Authorization", RR_USER_TOKEN)
req.Header.Set("Content-Type", "application/json")
resp, err := http.DefaultClient.Do(req)
if err != nil {
return err
}
resp.Body.Close()
if resp.StatusCode >= 400 {
return fmt.Errorf("failed to create rule: %d", resp.StatusCode)
}
}
return nil
}public void applyCustomerRules(
String customerId,
String proxyId,
Map<String, Object> ruleConfig
) throws IOException {
List<Map<String, Object>> rules = new ArrayList<>();
// Example: Rate limit per customer tier
if ("basic".equals(ruleConfig.get("tier"))) {
Map<String, Object> rule = new HashMap<>();
rule.put("effect", "deny");
rule.put("priority", 900);
rule.put("notes", "Basic tier rate limit");
rules.add(rule);
}
// Example: Restrict certain endpoints
if (ruleConfig.containsKey("restrictedPaths")) {
List<String> restrictedPaths = (List<String>) ruleConfig.get("restrictedPaths");
for (String path : restrictedPaths) {
Map<String, Object> rule = new HashMap<>();
rule.put("effect", "deny");
rule.put("priority", 800);
rule.put("pathPattern", path);
rule.put("pathPresence", "present");
rule.put("notes", "Restricted path for " + customerId);
rules.add(rule);
}
}
// Example: Method restrictions (read-only)
if (Boolean.TRUE.equals(ruleConfig.get("readOnly"))) {
Map<String, Object> rule = new HashMap<>();
rule.put("effect", "deny");
rule.put("priority", 850);
rule.put("methodPattern", "DELETE|PUT|PATCH");
rule.put("methodPresence", "present");
rule.put("notes", "Read-only access for " + customerId);
rules.add(rule);
}
// Create rules
String url = String.format(
"https://api.requestrocket.com/clients/%s/proxies/%s/rules",
SAAS_CLIENT_ID,
proxyId
);
for (Map<String, Object> rule : rules) {
String json = gson.toJson(rule);
RequestBody body = RequestBody.create(
json,
MediaType.parse("application/json")
);
Request request = new Request.Builder()
.url(url)
.header("Authorization", RR_USER_TOKEN)
.header("Content-Type", "application/json")
.post(body)
.build();
try (Response response = httpClient.newCall(request).execute()) {
if (!response.isSuccessful()) {
throw new IOException("Failed to create rule: " + response.code());
}
}
}
}Webhook Forwarding
Use Async API for reliable webhook delivery:
# Forward webhook using async endpoint
# Replace /api/ with /async/ in the proxy URL
curl -X POST "https://us-east-1.requestrocket.com/async/prx_customer123/webhooks" \
-H "Authorization: Bearer customer-proxy-api-key" \
-H "Content-Type: application/json" \
-d '{
"event": "order.created",
"orderId": "ord_123",
"timestamp": "2026-01-24T10:30:00Z",
"data": {
"amount": 99.99,
"currency": "USD"
}
}'
# Response includes requestId for tracking:
# {
# "requestId": "req_abc123xyz",
# "status": "queued"
# }
# Check webhook delivery status
curl -X GET "https://api.requestrocket.com/clients/$SAAS_CLIENT_ID/requests/req_abc123xyz" \
-H "Authorization: $RR_USER_TOKEN"async function forwardWebhook(customerId, platform, webhookData) {
const integration = await db.integrations.findOne({
customerId,
platform
});
// Use async endpoint for reliability
const asyncUrl = integration.proxyUrl.replace('/api/', '/async/');
const response = await fetch(`${asyncUrl}/webhooks`, {
method: 'POST',
headers: {
'Authorization': `Bearer ${integration.customerApiKey}`,
'Content-Type': 'application/json'
},
body: JSON.stringify(webhookData)
});
const { requestId } = await response.json();
// Track webhook delivery
await db.webhooks.create({
customerId,
platform,
requestId,
status: 'queued',
payload: webhookData
});
return requestId;
}def forward_webhook(customer_id, platform, webhook_data):
"""Forward webhook using async API for reliability"""
integration = db.integrations.find_one({
'customerId': customer_id,
'platform': platform
})
# Use async endpoint for reliability
async_url = integration['proxyUrl'].replace('/api/', '/async/')
response = requests.post(
f"{async_url}/webhooks",
headers={
'Authorization': f"Bearer {integration['customerApiKey']}",
'Content-Type': 'application/json'
},
json=webhook_data
)
result = response.json()
request_id = result['requestId']
# Track webhook delivery
db.webhooks.create({
'customerId': customer_id,
'platform': platform,
'requestId': request_id,
'status': 'queued',
'payload': webhook_data
})
return request_idfunc forwardWebhook(customerID, platform string, webhookData map[string]interface{}) (string, error) {
// Get integration
integration, err := db.Integrations.FindOne(map[string]interface{}{
"customerId": customerID,
"platform": platform,
})
if err != nil {
return "", err
}
// Use async endpoint for reliability
proxyURL := integration["proxyUrl"].(string)
asyncURL := strings.Replace(proxyURL, "/api/", "/async/", 1)
// Marshal webhook data
jsonData, err := json.Marshal(webhookData)
if err != nil {
return "", err
}
// Send to async endpoint
req, err := http.NewRequest(
"POST",
asyncURL+"/webhooks",
bytes.NewBuffer(jsonData),
)
if err != nil {
return "", err
}
req.Header.Set("Authorization", "Bearer "+integration["customerApiKey"].(string))
req.Header.Set("Content-Type", "application/json")
resp, err := http.DefaultClient.Do(req)
if err != nil {
return "", err
}
defer resp.Body.Close()
var result struct {
RequestID string `json:"requestId"`
}
if err := json.NewDecoder(resp.Body).Decode(&result); err != nil {
return "", err
}
// Track webhook delivery
err = db.Webhooks.Create(map[string]interface{}{
"customerId": customerID,
"platform": platform,
"requestId": result.RequestID,
"status": "queued",
"payload": webhookData,
})
return result.RequestID, err
}public String forwardWebhook(
String customerId,
String platform,
Map<String, Object> webhookData
) throws IOException {
// Get integration
Map<String, Object> integration = db.integrations.findOne(
Map.of("customerId", customerId, "platform", platform)
);
// Use async endpoint for reliability
String proxyUrl = (String) integration.get("proxyUrl");
String asyncUrl = proxyUrl.replace("/api/", "/async/");
// Send to async endpoint
String json = gson.toJson(webhookData);
RequestBody body = RequestBody.create(
json,
MediaType.parse("application/json")
);
Request request = new Request.Builder()
.url(asyncUrl + "/webhooks")
.header("Authorization", "Bearer " + integration.get("customerApiKey"))
.header("Content-Type", "application/json")
.post(body)
.build();
try (Response response = httpClient.newCall(request).execute()) {
String responseJson = response.body().string();
JsonObject result = gson.fromJson(responseJson, JsonObject.class);
String requestId = result.get("requestId").getAsString();
// Track webhook delivery
Map<String, Object> webhookRecord = new HashMap<>();
webhookRecord.put("customerId", customerId);
webhookRecord.put("platform", platform);
webhookRecord.put("requestId", requestId);
webhookRecord.put("status", "queued");
webhookRecord.put("payload", webhookData);
db.webhooks.create(webhookRecord);
return requestId;
}
}Monitoring Customer Usage
Track usage across all customer integrations:
# Get request logs for a specific proxy
curl -X GET "https://api.requestrocket.com/clients/$SAAS_CLIENT_ID/requests?proxyId=prx_customer123&startDate=2026-01-01&endDate=2026-01-31" \
-H "Authorization: $RR_USER_TOKEN"
# Response includes detailed request logs:
# {
# "data": [
# {
# "requestId": "req_abc123",
# "status": "completed",
# "duration": 234,
# "timestamp": "2026-01-24T10:30:00Z",
# ...
# }
# ]
# }
# Get aggregated stats for all customer integrations
# (Requires iterating through each customer's proxies)
for proxy_id in $(get_customer_proxy_ids); do
curl -X GET "https://api.requestrocket.com/clients/$SAAS_CLIENT_ID/requests?proxyId=$proxy_id&startDate=$START_DATE&endDate=$END_DATE" \
-H "Authorization: $RR_USER_TOKEN"
doneasync function getCustomerUsageStats(customerId, startDate, endDate) {
const integrations = await db.integrations.find({ customerId });
const stats = await Promise.all(
integrations.map(async (integration) => {
const response = await fetch(
`https://api.requestrocket.com/clients/${SAAS_CLIENT_ID}/requests?` +
`proxyId=${integration.proxyId}&startDate=${startDate}&endDate=${endDate}`,
{
headers: { 'Authorization': RR_USER_TOKEN }
}
);
const { data } = await response.json();
return {
platform: integration.platform,
totalRequests: data.length,
successful: data.filter(r => r.status === 'completed').length,
failed: data.filter(r => r.status === 'failed').length,
avgDuration: data.reduce((sum, r) => sum + r.duration, 0) / data.length
};
})
);
return {
customerId,
period: { startDate, endDate },
integrations: stats,
total: stats.reduce((sum, s) => sum + s.totalRequests, 0)
};
}def get_customer_usage_stats(customer_id, start_date, end_date):
"""Track usage across all customer integrations"""
integrations = db.integrations.find({'customerId': customer_id})
stats = []
for integration in integrations:
response = requests.get(
f"https://api.requestrocket.com/clients/{SAAS_CLIENT_ID}/requests",
params={
'proxyId': integration['proxyId'],
'startDate': start_date,
'endDate': end_date
},
headers={'Authorization': RR_USER_TOKEN}
)
data = response.json()['data']
total_requests = len(data)
successful = len([r for r in data if r['status'] == 'completed'])
failed = len([r for r in data if r['status'] == 'failed'])
avg_duration = sum(r['duration'] for r in data) / total_requests if total_requests > 0 else 0
stats.append({
'platform': integration['platform'],
'totalRequests': total_requests,
'successful': successful,
'failed': failed,
'avgDuration': avg_duration
})
return {
'customerId': customer_id,
'period': {'startDate': start_date, 'endDate': end_date},
'integrations': stats,
'total': sum(s['totalRequests'] for s in stats)
}type UsageStats struct {
Platform string `json:"platform"`
TotalRequests int `json:"totalRequests"`
Successful int `json:"successful"`
Failed int `json:"failed"`
AvgDuration float64 `json:"avgDuration"`
}
type CustomerUsage struct {
CustomerID string `json:"customerId"`
Period map[string]string `json:"period"`
Integrations []UsageStats `json:"integrations"`
Total int `json:"total"`
}
func getCustomerUsageStats(customerID, startDate, endDate string) (*CustomerUsage, error) {
integrations, err := db.Integrations.Find(map[string]interface{}{
"customerId": customerID,
})
if err != nil {
return nil, err
}
stats := []UsageStats{}
totalRequests := 0
for _, integration := range integrations {
url := fmt.Sprintf(
"https://api.requestrocket.com/clients/%s/requests?proxyId=%s&startDate=%s&endDate=%s",
SAAS_CLIENT_ID,
integration["proxyId"],
startDate,
endDate,
)
req, _ := http.NewRequest("GET", url, nil)
req.Header.Set("Authorization", RR_USER_TOKEN)
resp, err := http.DefaultClient.Do(req)
if err != nil {
return nil, err
}
defer resp.Body.Close()
var result struct {
Data []struct {
Status string `json:"status"`
Duration float64 `json:"duration"`
} `json:"data"`
}
json.NewDecoder(resp.Body).Decode(&result)
successful := 0
failed := 0
var totalDuration float64
for _, r := range result.Data {
if r.Status == "completed" {
successful++
} else if r.Status == "failed" {
failed++
}
totalDuration += r.Duration
}
avgDuration := float64(0)
if len(result.Data) > 0 {
avgDuration = totalDuration / float64(len(result.Data))
}
stat := UsageStats{
Platform: integration["platform"].(string),
TotalRequests: len(result.Data),
Successful: successful,
Failed: failed,
AvgDuration: avgDuration,
}
stats = append(stats, stat)
totalRequests += len(result.Data)
}
return &CustomerUsage{
CustomerID: customerID,
Period: map[string]string{
"startDate": startDate,
"endDate": endDate,
},
Integrations: stats,
Total: totalRequests,
}, nil
}public class UsageStats {
public String platform;
public int totalRequests;
public int successful;
public int failed;
public double avgDuration;
}
public class CustomerUsage {
public String customerId;
public Map<String, String> period;
public List<UsageStats> integrations;
public int total;
}
public CustomerUsage getCustomerUsageStats(
String customerId,
String startDate,
String endDate
) throws IOException {
List<Map<String, Object>> integrations = db.integrations.find(
Map.of("customerId", customerId)
);
List<UsageStats> stats = new ArrayList<>();
int totalRequests = 0;
for (Map<String, Object> integration : integrations) {
String url = String.format(
"https://api.requestrocket.com/clients/%s/requests?proxyId=%s&startDate=%s&endDate=%s",
SAAS_CLIENT_ID,
integration.get("proxyId"),
startDate,
endDate
);
Request request = new Request.Builder()
.url(url)
.header("Authorization", RR_USER_TOKEN)
.get()
.build();
try (Response response = httpClient.newCall(request).execute()) {
String json = response.body().string();
JsonObject result = gson.fromJson(json, JsonObject.class);
JsonArray data = result.getAsJsonArray("data");
int successful = 0;
int failed = 0;
double totalDuration = 0;
for (JsonElement element : data) {
JsonObject req = element.getAsJsonObject();
String status = req.get("status").getAsString();
if ("completed".equals(status)) {
successful++;
} else if ("failed".equals(status)) {
failed++;
}
totalDuration += req.get("duration").getAsDouble();
}
UsageStats stat = new UsageStats();
stat.platform = (String) integration.get("platform");
stat.totalRequests = data.size();
stat.successful = successful;
stat.failed = failed;
stat.avgDuration = data.size() > 0 ? totalDuration / data.size() : 0;
stats.add(stat);
totalRequests += data.size();
}
}
CustomerUsage usage = new CustomerUsage();
usage.customerId = customerId;
usage.period = Map.of("startDate", startDate, "endDate", endDate);
usage.integrations = stats;
usage.total = totalRequests;
return usage;
}Complete Example: Integration Marketplace
Here's a full example of building an integration marketplace:
// marketplace.js - Initialize your marketplace
class IntegrationMarketplace {
constructor(rrUserToken, saasClientId) {
this.rrToken = rrUserToken;
this.clientId = saasClientId;
}
async listAvailableIntegrations() {
return [
{
id: 'stripe',
name: 'Stripe',
description: 'Accept payments and manage subscriptions',
authType: 'oauth2',
targetUrl: 'https://api.stripe.com',
icon: '/icons/stripe.svg'
},
{
id: 'shopify',
name: 'Shopify',
description: 'Sync orders and inventory',
authType: 'apiKey',
targetUrl: 'https://api.shopify.com',
icon: '/icons/shopify.svg'
},
// ... more integrations
];
}
async ensureTarget(platformId, platformConfig) {
// Check if target exists
const response = await fetch(
`https://api.requestrocket.com/clients/${this.clientId}/targets`,
{
headers: { 'Authorization': this.rrToken }
}
);
const targets = await response.json();
const existing = targets.find(t => t.targetName === platformId);
if (existing) return existing;
// Create target
return await fetch(
`https://api.requestrocket.com/clients/${this.clientId}/targets`,
{
method: 'POST',
headers: {
'Authorization': this.rrToken,
'Content-Type': 'application/json'
},
body: JSON.stringify({
targetName: platformId,
region: 'us-east-1',
baseURL: platformConfig.targetUrl,
testPath: '/health'
})
}
).then(r => r.json());
}
}// Connect customer to integration
async function connectIntegration(customerId, platformId, credentials) {
const marketplace = new IntegrationMarketplace(
process.env.RR_TOKEN,
process.env.SAAS_CLIENT_ID
);
// Get platform config
const platforms = await marketplace.listAvailableIntegrations();
const platform = platforms.find(p => p.id === platformId);
if (!platform) {
throw new Error('Platform not found');
}
// Ensure target exists
const target = await marketplace.ensureTarget(platformId, platform);
// Create customer-specific proxy credential
const proxyKey = crypto.randomBytes(32).toString('hex');
const proxyCred = await createCredential(this.clientId, {
credentialName: `${customerId}-${platformId}-proxy`,
credentialType: 'proxy',
authType: 'apiKey',
region: 'us-east-1',
secret: {
keyName: 'X-API-Key',
keyValue: proxyKey
}
});
// Create target credential with customer's creds
const targetCred = await createCredential(this.clientId, {
credentialName: `${customerId}-${platformId}-target`,
credentialType: 'target',
authType: platform.authType,
region: 'us-east-1',
secret: credentials
});
// Create proxy
const proxy = await createProxy(this.clientId, {
proxyName: `${customerId}-${platformId}`,
region: 'us-east-1',
proxyCredentialId: proxyCred.credentialId,
targetId: target.targetId,
targetCredentialId: targetCred.credentialId,
active: true
});
// Save to database
await db.integrations.create({
customerId,
platformId,
platformName: platform.name,
proxyId: proxy.proxyId,
proxyUrl: proxy.baseURL,
apiKey: proxyKey,
status: 'active',
connectedAt: new Date()
});
return {
integration: {
platform: platform.name,
proxyUrl: proxy.baseURL,
apiKey: proxyKey
}
};
}// Sync data for customer
async function syncIntegrationData(customerId, platformId) {
const integration = await db.integrations.findOne({
customerId,
platformId,
status: 'active'
});
if (!integration) {
throw new Error('Integration not found or inactive');
}
try {
// Fetch data from third-party via proxy
const response = await fetch(
`${integration.proxyUrl}/api/data`,
{
headers: {
'Authorization': `Bearer ${integration.apiKey}`,
'Content-Type': 'application/json'
}
}
);
if (!response.ok) {
throw new Error(`Sync failed: ${response.status}`);
}
const data = await response.json();
// Process and store data
await processIntegrationData(customerId, platformId, data);
// Update sync status
await db.integrations.update(integration.id, {
lastSyncedAt: new Date(),
lastSyncStatus: 'success'
});
return { success: true, recordCount: data.length };
} catch (error) {
await db.integrations.update(integration.id, {
lastSyncedAt: new Date(),
lastSyncStatus: 'failed',
lastSyncError: error.message
});
throw error;
}
}// Dashboard endpoint for customer
app.get('/api/integrations/dashboard',
authenticateCustomer,
async (req, res) => {
const { customerId } = req.user;
// Get all customer integrations
const integrations = await db.integrations.find({ customerId });
// Get usage stats for each
const stats = await Promise.all(
integrations.map(async (integration) => {
const last24h = new Date(Date.now() - 24 * 60 * 60 * 1000);
const response = await fetch(
`https://api.requestrocket.com/clients/${SAAS_CLIENT_ID}/requests?` +
`proxyId=${integration.proxyId}&startDate=${last24h.toISOString()}`,
{
headers: { 'Authorization': process.env.RR_TOKEN }
}
);
const { data } = await response.json();
return {
platform: integration.platformName,
status: integration.status,
lastSync: integration.lastSyncedAt,
requests24h: data.length,
successRate: data.length > 0
? (data.filter(r => r.status === 'completed').length / data.length) * 100
: 100
};
})
);
res.json({
integrations: stats,
totalIntegrations: integrations.length,
activeIntegrations: integrations.filter(i => i.status === 'active').length
});
}
);Pricing Considerations
When building on RequestRocket:
- Plan for Scale: Request volumes add up across all customers
- Monitor Usage: Track usage per customer for potential charge-backs
- Consider Tiers: Different customer tiers may need different plans
- Cache Wisely: Reduce redundant requests to save costs
Security Checklist
- ✅ Never expose RequestRocket user tokens to customers
- ✅ Use unique proxy credentials per customer
- ✅ Implement authorization rules for access control
- ✅ Rotate credentials regularly
- ✅ Monitor for unusual activity
- ✅ Audit access logs periodically
- ✅ Encrypt data in transit and at rest
- ✅ Follow least privilege principle
Need help architecting your SaaS integration strategy? Contact our solutions team for consultation.