3rd-party APIs — overview
The universal pattern for calling external services from your FlareX app — Secrets, fetch, retries, rate limits, and error handling.
Updated
Most apps eventually need to talk to something else — Stripe for payments, OpenAI for AI, Google for spreadsheets, your CRM, your analytics provider. The pattern is the same regardless of which service:
- Get an API key from the provider's dashboard.
- Add it to Secrets in FlareX (encrypted at rest).
- Tell FlareX what to do with it.
- Handle failure modes (rate limits, transient errors, bad data).
This page covers steps 2–4. Steps 1 and 3 depend on the service — see the per-service walkthroughs at the bottom of this page.
The phrase "3rd-party API" here means HTTP APIs — REST or RPC. SDK libraries (Stripe Node SDK, the official OpenAI client, etc.) are wrappers around these APIs and follow the same pattern.
Step 1: Add the credential to Secrets
Get the credential from the provider
Sign in to the provider's dashboard, find the API keys section, and create a new key. Copy it once — most providers only show it on creation.
For production apps, name the key something specific so you can revoke it later: e.g.,
flarex-prod-2026-04-25rather thankey-1.Open Secrets in your FlareX app
Panel → your app → Secrets.
Add the secret
Pick a clear name. Conventions:
STRIPE_SECRET_KEYfor StripeOPENAI_API_KEYfor OpenAIANTHROPIC_API_KEYfor Anthropic<SERVICE>_API_KEYfor everything else
Paste the value and save. The plaintext is encrypted to the workspace's encryption key and never shown again.
See Secrets for the full lifecycle — rotation, scoping, deletion.
Step 2: Tell FlareX to use it
Describe the integration in chat. FlareX writes the fetch wrapper, picks a sensible client library if one exists, and adds error handling.
Add a Stripe integration. Use STRIPE_SECRET_KEY from Secrets.
On POST /checkout, create a Checkout Session for $9.99 USD and return
the session URL.
Wire in OpenAI. Use OPENAI_API_KEY from Secrets.
Add a /summarize endpoint that takes { text: string } and returns
{ summary: string } using gpt-4o-mini.
Step 3: Handle the failure modes
Production calls to 3rd-party APIs fail. Often. Plan for it.
Transient errors → retry with backoff
Network blips, 502s, 503s, and 5xx errors are usually transient. Retry with exponential backoff and a cap:
async function fetchWithRetry(url: string, init: RequestInit, max = 3) {
for (let attempt = 0; attempt < max; attempt++) {
const res = await fetch(url, init);
if (res.ok) return res;
if (res.status >= 500 && attempt < max - 1) {
const delay = Math.min(1000 * 2 ** attempt, 8000);
await new Promise((r) => setTimeout(r, delay));
continue;
}
return res;
}
throw new Error('unreachable');
}
Ask FlareX to use this pattern; it will.
Rate limits → respect the headers
Most modern APIs return Retry-After (seconds) or X-RateLimit-Reset (timestamp) when they throttle you. Sleep until then before retrying.
if (res.status === 429) {
const after = Number(res.headers.get('retry-after') ?? 1);
await new Promise((r) => setTimeout(r, after * 1000));
// retry once
}
Bad data → validate at the boundary
The API claims to return JSON shaped like X. Sometimes it doesn't. Use Zod or a similar validator at the API boundary so a bad response surfaces as a clear error rather than a cannot read property 'foo' of undefined 200ms later:
const ProfileSchema = z.object({
id: z.string(),
email: z.string().email(),
plan: z.enum(['free', 'pro']),
});
const profile = ProfileSchema.parse(await res.json());
Timeout
Default fetch in Node has no timeout. Always set one:
const ctrl = new AbortController();
const timeout = setTimeout(() => ctrl.abort(), 10_000);
try {
const res = await fetch(url, { signal: ctrl.signal, ...init });
} finally {
clearTimeout(timeout);
}
Step 4: Cache when you can
Hitting an external API on every page load is slow and counts against rate limits. Cache by the smallest sensible key:
import { LRUCache } from 'lru-cache';
const cache = new LRUCache<string, T>({ max: 500, ttl: 60_000 });
async function getProfile(userId: string) {
const hit = cache.get(userId);
if (hit) return hit;
const fresh = await fetchProfile(userId);
cache.set(userId, fresh);
return fresh;
}
For data shared across requests, cache in Redis (auto-injected as REDIS_URL on paid plans):
const cached = await redis.get(`profile:${userId}`);
if (cached) return JSON.parse(cached);
const fresh = await fetchProfile(userId);
await redis.setex(`profile:${userId}`, 300, JSON.stringify(fresh));
return fresh;
SSRF safety
If your app calls URLs provided by users (image proxies, link previews, webhook tests), be careful: a user-supplied URL pointing at http://169.254.169.254/ or http://localhost:6379/ can read your cloud metadata or your Redis.
FlareX's shared safe-dispatcher re-verifies resolved IPs against private/loopback/link-local ranges and refuses outbound connections to them. Ask for it explicitly:
For the link-preview endpoint, fetch the user-provided URL using the
SSRF-safe dispatcher pattern — block private IPs, loopback,
link-local, and metadata endpoints. Cap the response size at 2 MB.
Per-service walkthroughs
Common integrations with worked examples:
- Stripe — Checkout, subscriptions, webhooks
- OpenAI —
/v1/chat/completions, streaming, retries - Google APIs — OAuth, Sheets, Calendar
- Discord webhooks — incoming Discord webhooks (separate from bots)
If you don't see your provider, the patterns above apply to all of them. Tell FlareX the service, your auth scheme, and what you want to do.