Developers
AI Gateway API
The Al-Maqdisi AI Gateway is a secure proxy between client applications and the Gemini API. Apps never hold a provider key — they name a pre-approved task, and the gateway applies the model, limits, and safety settings server-side.
https://api.almaqdisiproductions.com Overview
Embedding an AI provider key inside a mobile app makes it publicly extractable — keys shipped in binaries are routinely harvested and abused. The gateway removes the key from the client entirely: the app sends a task name and a message, and the gateway holds the provider key encrypted server-side, enforces a per-app task allowlist, applies rate limits, and streams the model's answer back.
- Task allowlist — a client can only name a task. The model, output cap, streaming/JSON permission, temperature ceiling, and safety settings are decided server-side per task. Capabilities outside the allowlist (e.g. image generation, arbitrary model selection) do not exist on this API surface.
- Two auth modes per app —
publicfor mobile clients (app id only; nothing secret ships in the binary) andtokenfor server-to-server callers (a revocable bearer token kept in server secrets). - Operator controls — keys, tasks, tokens, rate limits, and a per-app kill switch are managed at runtime in a private portal; no client release is needed to rotate a key or pause traffic.
Authentication
Every request carries X-App-Id. Whether a bearer
token is also required depends on the app's operator-configured auth mode:
| Mode | Required headers | Intended callers |
|---|---|---|
public | X-App-Id only | Mobile apps — nothing secret is provisioned at build time; protection comes from the task allowlist, rate limits, and the operator kill switch |
token | X-App-Id + Authorization: Bearer <app token> | Server-to-server callers whose token stays a true secret (backend env / secrets manager) |
POST /v1/invoke
The single proxy endpoint. Send a task name and a message; receive the model's answer as JSON or as a Server-Sent-Events stream.
Request headers
# public mode
X-App-Id: your-app-id
Content-Type: application/json
# token mode
X-App-Id: your-app-id
Authorization: Bearer amp_your-app-id_XXXXXXXX
Content-Type: application/json Request body
| Field | Type | Required | Notes |
|---|---|---|---|
task | string | Yes | Names an allowlisted task (e.g. chat). The server decides the model, output caps, and safety settings for it — the client cannot override them. |
message | string | Yes | The user message. Clamped server-side to the task's input limit. |
systemInstruction | string | No | System prompt; accepted only if the task permits one. |
history | array | No | Prior turns as { role: "user" | "agent", text }. Only the most recent turns within the task's limit are kept. |
generationConfig | object | No | temperature, maxOutputTokens, topP, responseMimeType — every field is clamped to the task's server-side ceilings. |
stream | boolean | No | Request an SSE stream. Honored only if the task allows streaming. |
clientId | string | No | A stable anonymous identifier (e.g. an install UUID) used for fair per-client rate limiting. Falls back to IP when absent. |
Example request
{
"task": "chat",
"message": "Summarize this in one sentence: …",
"history": [
{ "role": "user", "text": "…" },
{ "role": "agent", "text": "…" }
],
"generationConfig": { "temperature": 0.3, "maxOutputTokens": 1024 },
"stream": false,
"clientId": "6f1c9c1e-anon-install-uuid"
} Response — single-shot
{
"text": "…the model's answer…",
"usage": { "promptTokens": 132, "outputTokens": 456 }
} Streaming
With "stream": true (on tasks that allow it) the
response is text/event-stream. Each frame is a JSON object:
data: {"token":"The "}
data: {"token":"answer "}
data: {"token":"continues…"}
data: {"done":true}
# on a mid-stream failure the gateway emits one error frame, then closes:
data: {"error":"upstream_error"}
Concatenate token values in order; treat done as
end-of-message; surface error frames through your app's normal
degraded-state handling.
GET /v1/health
Unauthenticated liveness probe.
{ "ok": true, "service": "almaqdisi-ai-gateway", "ts": "2026-07-18T12:00:00.000Z" } Errors
Every non-2xx response uses one standardized envelope. Branch on
code, never on message:
{ "error": { "code": "rate_limited", "message": "Rate limit exceeded. Retry later." } } | Code | HTTP | Meaning |
|---|---|---|
bad_request | 400 | Malformed body / missing required field |
message_required | 400 | message empty after trimming |
unknown_task | 400 | Task not registered for this app (the allowlist said no) |
stream_not_allowed | 400 | stream: true on a task that does not permit streaming |
unauthenticated | 401 | Missing bearer token or X-App-Id |
invalid_token | 401 | Token unknown, revoked, or wrong for this app |
task_disabled | 403 | Task exists but is currently switched off |
safety_blocked | 422 | The AI provider refused the content |
rate_limited | 429 | Per-client or per-app window exhausted (Retry-After header set) |
upstream_error | 502 | The AI provider returned an error (also surfaces provider 429s) |
app_disabled | 503 | The app's proxy is paused by the operator |
no_active_key | 503 | No active provider key is configured for this app |
internal | 500 | Gateway error |
Rate limits
Two fixed windows are checked on every request, and both must pass:
- Per client — each task has a per-minute allowance, bucketed by
clientId(or IP when absent). Send a stable anonymousclientIdso one heavy user cannot exhaust the shared IP bucket. - Per app — a global per-minute ceiling across all of an app's clients.
A 429 response includes a
Retry-After header (seconds). Back off and retry after that interval.
Code examples
Mobile app — public mode (zero build-time credentials)
const res = await fetch('https://api.almaqdisiproductions.com/v1/invoke', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-App-Id': 'your-app-id', // the only identity the build carries
},
body: JSON.stringify({ task: 'chat', message, history, stream: true }),
}); Server-to-server — token mode
const res = await fetch(`${process.env.AI_GATEWAY_URL}/v1/invoke`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
Authorization: `Bearer ${process.env.AI_GATEWAY_APP_TOKEN}`,
'X-App-Id': 'your-app-id',
},
body: JSON.stringify({ task: 'ragAnswer', message: combinedPrompt }),
});
const { text } = await res.json(); Getting access
Apps are onboarded by the gateway operator through a private management portal — registration, task allowlists, provider keys, tokens, and traffic insight are all managed there. The gateway currently serves Al-Maqdisi Productions apps; if you are building with us or want the same architecture for your own apps, get in touch.
Building your own? The full architecture and an end-to-end implementation guide are documented in our open playbook, API-Key-Gateway-Proxy-Architecture.