Skip to content

Admin API Reference

The gateway ships a built-in REST API under /admin/api/ that powers the Admin Panel and can be consumed by scripts or external tooling. All endpoints return JSON unless otherwise noted.

Base path

Every endpoint below is relative to the gateway's listen address, e.g. http://localhost:8765.


Authentication

Check auth requirement

Check whether the admin panel requires a password.

GET /admin/api/auth-check

Response

{"requires_auth": true}
Field Type Description
requires_auth bool true when server.admin_password is set in the config.

Login

Validate the admin password and obtain a session token. Rate-limited to 5 failures per IP; the IP is locked out for 5 minutes after the threshold.

POST /admin/api/login

Request body

{"password": "my-admin-password"}

Success response

{"ok": true, "token": "<session-token>"}

Error responses

Status Body Condition
400 {"error": "Admin password not configured"} No server.admin_password in config
400 {"error": "Invalid JSON body"} Malformed request body
401 {"error": "Invalid password"} Wrong password
429 {"error": "Too many failed attempts. Try again in Ns."} Rate-limited

Configuration

Get config

Return the full gateway configuration with masked API keys.

GET /admin/api/config

Response

{
  "config_path": "/home/user/.config/llm-rosetta-gateway/config.jsonc",
  "providers": {
    "openai": {
      "api_key": "sk-a***abcd",
      "base_url": "https://api.openai.com/v1",
      "type": "openai_chat"
    }
  },
  "models": {
    "gpt-4o": {
      "provider": "openai",
      "capabilities": ["text", "vision", "tools"]
    }
  },
  "server": {"proxy": "http://proxy:8080"},
  "credential_visible": false,
  "known_provider_types": ["openai_chat", "openai_responses", "anthropic", "google"],
  "registered_shims": [
    {
      "name": "openai",
      "base": "openai_chat",
      "logo": "https://...",
      "default_base_url": "https://api.openai.com/v1",
      "default_api_key_env": "OPENAI_API_KEY"
    }
  ]
}

Key masking

Literal API keys are masked (sk-a***abcd). Environment variable placeholders like ${OPENAI_API_KEY} are returned as-is.


Add / update provider

Create a new provider or update an existing one. Supports renaming (with automatic model reference updates).

PUT /admin/api/config/providers/<name>

Request body

Field Type Required Description
api_key string Yes* Provider API key. Omit or send empty to keep the existing key when editing.
base_url string Yes Provider base URL.
type string No API standard or shim name (e.g. openai_chat, anthropic).
proxy string No Per-provider proxy URL.
rename_from string No Original name when renaming — old entry is removed and model refs updated.

Success response

{
  "ok": true,
  "provider": "openai",
  "providers": ["openai", "anthropic"]
}

Errors: 400 (missing fields, invalid JSON), 404 (rename source not found), 409 (rename target exists), 500 (disk / reload failure).


Delete provider

DELETE /admin/api/config/providers/<name>?cascade=true
Query param Type Default Description
cascade "true" | "1" Also delete models that reference this provider.

Success response

{
  "ok": true,
  "deleted": "openai",
  "providers": ["anthropic"],
  "cascade_deleted_models": ["gpt-4o"]
}

Errors: 404 (not found), 409 (models still reference this provider and cascade not set).


Toggle provider

Enable or disable a provider without removing it.

POST /admin/api/config/providers/<name>/toggle

Response

{"ok": true, "provider": "openai", "enabled": false}

Add / update model

PUT /admin/api/config/models/<name>

Request body

Field Type Required Description
provider string Yes Target provider name (must exist).
capabilities string[] No List of capabilities. Defaults to ["text"].
upstream_model string No Upstream model ID if different from the gateway-facing name.
rename_from string No Original name when renaming.

Success response

{
  "ok": true,
  "model": "gpt-4o",
  "provider": "openai",
  "capabilities": ["text", "vision", "tools"],
  "models": {"gpt-4o": "openai"}
}

Delete model

DELETE /admin/api/config/models/<name>

Response

{"ok": true, "deleted": "gpt-4o", "models": {}}

Update server settings

Currently supports the global proxy setting.

PUT /admin/api/config/server

Request body

{"proxy": "http://proxy:8080"}

Send an empty string ("") to remove the proxy.

Response

{"ok": true, "server": {"proxy": "http://proxy:8080"}}

Reload config

Force a hot-reload of the config from disk, without restarting the gateway.

POST /admin/api/config/reload

Response

{
  "ok": true,
  "providers": ["openai", "anthropic"],
  "models": {"gpt-4o": "openai"}
}

Fetch upstream models

Query a provider's /v1/models endpoint to discover available models.

GET /admin/api/config/providers/<name>/models

Response

{
  "provider": "openai",
  "api_standard": "openai_chat",
  "models": ["gpt-4o", "gpt-4o-mini", "o3-mini"]
}

Errors: 404 (provider not found), 502 (upstream connection or HTTP error).


Bulk-add models

Add multiple models for a provider in a single call.

POST /admin/api/config/models

Request body

Field Type Required Description
provider string Yes Target provider name.
models string[] Yes List of model IDs to add.
prefix string No Prefix prepended to each model name. When used, the original ID is stored as upstream_model.
capabilities string[] No Capabilities for all added models. Defaults to ["text", "vision", "tools"].

Response

{
  "ok": true,
  "added": ["my/gpt-4o", "my/gpt-4o-mini"],
  "skipped": ["my/o3-mini"],
  "models": {"my/gpt-4o": "openai", "my/gpt-4o-mini": "openai"}
}

Reveal provider API key

Return the unmasked API key for a provider. Only available when server.credential_visible is true in the config.

GET /admin/api/config/providers/<name>/key

Response

{"api_key": "sk-abc123..."}

Errors: 403 (credential visibility disabled), 404 (provider not found).


API Key Management

List keys

Return all gateway API keys with masked values.

GET /admin/api/keys

Response

{
  "keys": [
    {
      "id": "a1b2c3d4",
      "key": "rsk-***abcd",
      "label": "production",
      "created": "2025-01-15T10:30:00+00:00"
    }
  ]
}

Legacy single key

If the config uses the older server.api_key field instead of server.api_keys, it is returned as a synthetic entry with id: "default".


Create key

Generate a new gateway API key (or register a manually specified one).

POST /admin/api/keys

Request body

Field Type Required Description
label string No Human-readable label.
key string No Manual key value. If omitted, a random rsk-<hex> key is generated.

Response

{
  "ok": true,
  "key": {
    "id": "e5f6a7b8",
    "key": "rsk-0123456789abcdef0123456789abcdef",
    "label": "staging",
    "created": "2025-01-15T12:00:00+00:00"
  }
}

Warning

The full key value is returned only once in this response. Subsequent GET /admin/api/keys calls return masked values.


Update key label

PUT /admin/api/keys/<key_id>

Request body

{"label": "new-label"}

Response

{"ok": true, "id": "a1b2c3d4", "label": "new-label"}

Delete key

DELETE /admin/api/keys/<key_id>

Response

{"ok": true, "deleted": "a1b2c3d4"}

Reveal key

Return the unmasked value of a specific API key. Requires server.credential_visible: true.

GET /admin/api/keys/<key_id>/reveal

Response

{"key": "rsk-0123456789abcdef0123456789abcdef"}

Errors: 403 (credential visibility disabled), 404 (key not found).


Get internal token

Return the ephemeral internal token used by the admin panel to make test requests through the gateway.

GET /admin/api/internal-token

Response

{"token": "internal-abc123"}

Observability

Metrics snapshot

Return real-time gateway metrics including counters, breakdowns, and a rolling time-series.

GET /admin/api/metrics?seconds=60
Query param Type Default Range Description
seconds int 60 1–300 Length of the time-series window.

Response

{
  "uptime_seconds": 3600.5,
  "total_requests": 1234,
  "total_errors": 12,
  "total_streams": 456,
  "error_rate": 0.0097,
  "active_streams": 3,
  "by_model": {"gpt-4o": 800, "claude-sonnet-4-20250514": 434},
  "by_source_provider": {"openai_chat": 1000, "anthropic": 234},
  "by_target_provider": {"openai": 800, "anthropic": 434},
  "by_status_code": {"200": 1222, "500": 12},
  "series": [
    {"t": -59, "count": 2, "avg_ms": 450.12, "errors": 0},
    {"t": -58, "count": 0, "avg_ms": 0, "errors": 0}
  ]
}
Series field Description
t Negative offset in seconds (e.g. -59 = 59 seconds ago).
count Requests in that second.
avg_ms Average latency (ms).
errors Number of error responses (status >= 400).

Request log

Return paginated, filtered request log entries (newest first).

GET /admin/api/requests?limit=50&offset=0&model=gpt-4o&provider=openai&status=ok
Query param Type Default Description
limit int 50 Page size.
offset int 0 Number of entries to skip.
model string Filter by model name.
provider string Filter by target provider.
status "ok" | "error" ok = 2xx/3xx, error = 4xx/5xx.

Response

{
  "entries": [
    {
      "id": "a1b2c3d4e5f6...",
      "timestamp": "2025-01-15T12:00:00+00:00",
      "model": "gpt-4o",
      "source_provider": "openai_chat",
      "target_provider": "openai",
      "is_stream": false,
      "status_code": 200,
      "duration_ms": 1234.56,
      "api_key_label": "production"
    }
  ],
  "total": 100
}

Clear request log

DELETE /admin/api/requests

Response

{"ok": true}

Network diagnostics

Run IP geolocation and Google connectivity checks through the gateway's configured proxy (if any).

GET /admin/api/diagnostics/network

Response

{
  "proxy": "http://proxy:8080",
  "ip": {
    "ok": true,
    "ip": "203.0.113.42",
    "country": "United States",
    "city": "San Francisco",
    "isp": "Example ISP"
  },
  "google": {
    "ok": true,
    "status": 204
  }
}

The proxy field is present only when a global proxy is configured. Each sub-object includes "ok": false and an "error" message on failure.


Rebuild metrics

Rebuild all metrics counters from the persisted request log. Useful after fixing a counter bug or when in-memory counters have drifted from the actual log data.

POST /admin/api/metrics/rebuild

Response

{
  "ok": true,
  "rebuilt_from": 1234,
  "counters": {
    "total_requests": 1234,
    "total_errors": 12,
    "total_streams": 456
  }
}
Field Type Description
rebuilt_from int Number of log entries used to rebuild the counters.
counters object The rebuilt counter values (immediately persisted).

Errors: 400 (no persistence configured — gateway is running in memory-only mode).


Profiling

On-demand deep profiling powered by pyinstrument. When enabled, the gateway profiles the next N requests and stores interactive flamegraph results.

Optional dependency

Profiling requires pyinstrument. Install with: pip install llm-rosetta[profiling]

Get status

Return the current profiling state.

GET /admin/api/profiling/status

Response

{
  "enabled": false,
  "remaining": 0,
  "results_count": 3,
  "max_results": 20
}
Field Type Description
enabled bool Whether profiling is currently active.
remaining int Number of requests left to profile.
results_count int Number of stored profiling results.
max_results int Maximum number of results kept (oldest are trimmed).

Enable profiling

Enable profiling for the next N requests.

POST /admin/api/profiling/enable

Request body

Field Type Default Description
requests int 5 Number of requests to profile. Clamped to 1–100.

Response

{
  "enabled": true,
  "remaining": 10,
  "results_count": 0,
  "max_results": 20
}

Errors: 400 (pyinstrument is not installed).


Disable profiling

Manually disable profiling before the remaining counter reaches zero.

POST /admin/api/profiling/disable

Response

{
  "enabled": false,
  "remaining": 0,
  "results_count": 3,
  "max_results": 20
}

List results

Return summaries of all stored profiling results (without the full HTML/text bodies to keep the response compact).

GET /admin/api/profiling/results

Response

{
  "results": [
    {
      "timestamp": "2025-01-15T12:00:00+00:00",
      "request_id": "a1b2c3d4",
      "model": "gpt-4o",
      "source": "openai_chat",
      "target": "openai",
      "is_stream": false,
      "duration_ms": 1234.56
    }
  ],
  "total": 1
}

Get single result

Return a single profiling result by index (0-based).

GET /admin/api/profiling/results/<index>
Query param Type Default Description
format "html" | "json" "json" html returns the interactive flamegraph directly with text/html content type.

Response (JSON, default)

Returns the full result object including html and text fields.

Response (HTML)

Returns the pyinstrument flamegraph HTML page directly (content type text/html), suitable for viewing in a browser.

Errors: 400 (invalid index), 404 (index out of range).


Download all results

Download all profiling results as a ZIP archive. Each result is saved as an HTML flamegraph file named profile-<index>-<model>-<timestamp>.html.

GET /admin/api/profiling/results/download

Returns a application/zip response with Content-Disposition: attachment.

Errors: 404 (no results to download).


Clear results

Delete all stored profiling results.

DELETE /admin/api/profiling/results

Response

{"ok": true}

Error Dumps

When the gateway encounters an error during request processing, it can persist detailed diagnostic dumps including the original request body, converted body, error details, and timing information. Error dumps require persistence to be configured.

List error dumps

Return paginated, filtered error dump entries (newest first).

GET /admin/api/error-dumps?limit=50&offset=0
Query param Type Default Description
limit int 50 Page size.
offset int 0 Number of entries to skip.
model string Filter by model name.
error_phase string Filter by the phase where the error occurred.
provider string Filter by target provider.

Response

{
  "entries": [
    {
      "id": "d1e2f3a4",
      "timestamp": "2025-01-15T12:00:00+00:00",
      "model": "gpt-4o",
      "provider": "openai",
      "error_phase": "upstream",
      "error_message": "Connection refused",
      "status_code": 502,
      "body_hash": "abc123..."
    }
  ],
  "total": 42
}

Errors: 400 (no persistence configured).


Get error dump detail

Return a single error dump with decompressed request and converted bodies.

GET /admin/api/error-dumps/<dump_id>

Response

{
  "id": "d1e2f3a4",
  "timestamp": "2025-01-15T12:00:00+00:00",
  "model": "gpt-4o",
  "provider": "openai",
  "error_phase": "upstream",
  "error_message": "Connection refused",
  "status_code": 502,
  "body_hash": "abc123...",
  "request_body": {"model": "gpt-4o", "messages": [...]},
  "converted_body_hash": "def456...",
  "converted_body": {"model": "gpt-4o", "messages": [...]}
}

The request_body and converted_body fields contain the decompressed JSON objects. They are null if decompression fails or the body was not stored.

Errors: 400 (no persistence configured), 404 (dump not found).


Download request body

Download the raw decompressed request body JSON for an error dump as a file.

GET /admin/api/error-dumps/<dump_id>/body

Returns a application/json response with Content-Disposition: attachment; filename="error-dump-<dump_id>.json".

Errors: 400 (no persistence configured), 404 (dump not found or no body stored).


Clear error dumps

Delete all stored error dumps.

DELETE /admin/api/error-dumps

Response

{"ok": true}

Errors: 400 (no persistence configured).


Model Testing

The admin API runs model tests asynchronously. POST starts a task, then poll GET for the result.

Start test

POST /admin/api/test

Request body

Field Type Required Description
endpoint string Yes Gateway endpoint to call, e.g. /v1/chat/completions.
payload object Yes Request body forwarded to the endpoint.

Response

{"task_id": "a1b2c3d4e5f6"}

The test request is routed through the gateway itself using an ephemeral internal token, so it exercises the full proxy pipeline.


Poll test result

GET /admin/api/test/<task_id>

Response (pending)

{"status": "pending"}

Response (completed)

{
  "status": "done",
  "status_code": 200,
  "body": {"id": "chatcmpl-...", "choices": [...]}
}

Response (error)

{"status": "error", "error": "Connection refused"}
Status value Meaning
pending Task is still running.
done Upstream responded; check status_code and body.
error Exception during the request.
cancelled Task was cancelled via DELETE.

Tasks are automatically cleaned up after 5 minutes.


Cancel test

DELETE /admin/api/test/<task_id>

Response

{"ok": true}

Common error format

All endpoints use a consistent error envelope:

{"error": "Human-readable error message"}

Endpoints that write to disk may return a partial-success response when the config was saved but the hot-reload failed:

{
  "error": "Config saved but reload failed: ...",
  "saved": true,
  "reloaded": false
}