Documentation
FrenyTech API Console
A reference for the console's single proxy endpoint, its sanitized response contract, error handling and security guarantees.
Overview
The console never contacts the upstream API from the browser. Every request goes to /api/generate on the FrenyTech server, which performs a plain GET against the configured upstream URL, validates the reply, redacts credential material and returns a typed JSON envelope.
Browser ──GET /api/generate──▶ FrenyTech server ──GET UPSTREAM_API_URL──▶ Upstream API
◀── sanitized JSON ── │ validate · redact · rate limit · timeoutEndpoint
Method
GET
Path
/api/generate
Parameters
None. No query string, no body.
curl -s https://your-domain.example/api/generate \
-H "accept: application/json"Responses are returned with Cache-Control: no-store, X-Content-Type-Options: nosniff and X-Robots-Tag: noindex. An alias exists at /api/public/generate with identical behaviour.
Response format
A successful call returns ok: true with two views of the data: a fully sanitized mirror of the upstream payload and a flattened summary used by the dashboard cards. Values shown below are illustrative — the real response depends on the upstream API at request time.
{
"ok": true,
"sanitized": {
"success": true,
"data": {
"token": "[Credential redacted]",
"expiry": "2026-09-14T17:04:11.000Z",
"type": "cookie",
"profile": { "country": "US", "plan": "Premium" },
"links": { "login": "[Credential URL redacted]" },
"lifetime_seconds": 86400,
"lifetime_formatted": "24h 0m 0s",
"is_valid": true,
"generated_at": "2026-09-13T17:04:11.000Z"
}
},
"summary": {
"success": true,
"isValid": true,
"type": "cookie",
"country": "US",
"plan": "Premium",
"lifetimeFormatted": "24h 0m 0s",
"lifetimeSeconds": 86400,
"expiry": "2026-09-14T17:04:11.000Z",
"generatedAt": "2026-09-13T17:04:11.000Z",
"protectedFields": ["data.token", "data.links.login"]
},
"requestedAt": "2026-09-13T17:04:10.812Z",
"durationMs": 642
}summary.protectedFieldslists the dotted paths that were redacted so the UI can explain what was withheld.- Redacted strings are always exactly
"[Credential redacted]"or"[Credential URL redacted]". durationMsmeasures the full server round trip, including the upstream call.
Error codes
Failures use the same envelope with ok: false. Messages are user-safe; raw upstream error bodies are never forwarded.
{
"ok": false,
"error": {
"code": "rate_limited",
"title": "Too many requests",
"message": "You are generating too quickly. Please wait a moment and try again.",
"retryAfterSeconds": 3
},
"requestedAt": "2026-09-13T17:04:10.812Z"
}| Code | HTTP | When |
|---|---|---|
| rate_limited | 429 | Too many requests from the same client. Includes retryAfterSeconds and a Retry-After header. |
| timeout | 504 | The upstream API did not answer within 15 seconds. |
| upstream_unavailable | 502 | The upstream host could not be reached (DNS, TLS or network failure). |
| upstream_error | 502 | The upstream API answered with a non-2xx status. The status is included as error.status. |
| invalid_response | 502 | The upstream body was not a JSON object. |
| method_not_allowed | 405 | Any method other than GET was used. |
| server_error | — | Raised client-side when the console cannot reach its own proxy. |
Security model
Upstream URL & key stay server-side
UPSTREAM_API_URL and the optional UPSTREAM_API_KEY are read inside the server handler only. They are never bundled for the browser.
Redaction before serialization
Keys such as token, cookie, password, secret, session and authorization — plus any URL carrying credential-like query parameters — are replaced recursively before the JSON response is built.
Rate limiting
10 requests per minute per client with a 3-second minimum interval. Excess calls receive 429 with Retry-After.
Timeouts
Upstream calls abort after 15 seconds and return a 504 timeout error instead of hanging.
No sensitive persistence
Request history stores only time, status, validity, country, plan and lifetime in local storage.
Hardened responses
API responses are no-store, nosniff and noindex. Pages ship security headers including a Content-Security-Policy in production.
Configuration
Deployment needs one required server variable. The site URL improves canonical links and social previews but is optional.
# Server-only — never exposed to the browser
UPSTREAM_API_URL=https://example-upstream.invalid/generate
UPSTREAM_API_KEY= # optional; sent as Authorization: Bearer
# Public — used for canonical URLs, sitemap and Open Graph
VITE_SITE_URL=https://console.frenytech.comOn Vercel, add the variables in Project → Settings → Environment Variables. The included vercel.json selects the Vercel server preset automatically.