Skip to main content

Responses Proxy API reference

The hosted backend service is:

https://agent-responses-proxy.svc.cloud.morph.so/

Responses-compatible clients should use:

https://agent-responses-proxy.svc.cloud.morph.so/v1

The live OpenAPI schema is available from:

GET /docs
GET /openapi.json

Public routes

GET /healthz
GET /readyz
POST /v1/responses
GET|POST|DELETE|PATCH /v1/responses/{relative_path}
WS /v1/responses
POST /v1/messages
POST /v1/messages/count_tokens
GET /v1/models
GET /docs
GET /openapi.json

POST /v1/responses creates a proxied response. It requires an org-scoped proxy key as Authorization: Bearer <key>. The body must include model with a registered alias in that key's org. Missing or invalid proxy key returns 401, empty registry returns 503, missing model returns 400, and unknown alias returns 404.

On retryable upstream failures (429, 502, 503, 504, connection errors, or timeouts), the proxy can fail over to another healthy resource in the same org pool up to PROXY_UPSTREAM_MAX_ATTEMPTS attempts. Non-429 4xx errors and known quota or balance failures are returned unchanged. Streaming requests only fail over before response bytes are sent, and affinity-pinned continuations are not failed over.

Subpaths under /v1/responses/{relative_path} proxy Responses API follow-up operations. Continuations with previous_response_id or resp_* path segments route by affinity.

The WebSocket route uses model alias query or header metadata to select the provider pool.

The Claude Code gateway routes expose an Anthropic Messages-compatible surface. They require the org-scoped proxy key in X-Responses-Proxy-Key by default. GET /v1/models returns only the caller org's enabled Claude Code-compatible anthropic_messages, responses, and deployment-enabled chat_completions aliases, plus deployment-configured direct models. Claude gateway request bodies larger than PROXY_CLAUDE_GATEWAY_MAX_BODY_BYTES return 413 request_too_large.

Admin auth

Every /admin/* route requires Morph bearer auth except Claude Code OAuth setup completion, which uses a one-time setup token. There is no static admin token. Direct operator calls use a Morph org token. The Morph Cloud frontend BFF and deploy automation use a Morph service delegation token.

Authorization: Bearer <SEELE_ORG_TOKEN_OR_DELEGATION_TOKEN>

Providers, model aliases, and proxy keys are org-scoped. Missing or invalid credentials return 401, valid credentials without allowed org membership return 403, and admin auth misconfiguration or dependency failures return 503.

When using the Morph Cloud console, the browser calls a same-origin BFF. The BFF obtains the service delegation token server-side; the browser never receives admin credentials.

Admin state and telemetry

GET /admin/state
GET /admin/events
GET /admin/overview?limit=100&session_id=...&resource_id=...&provider_id=...
GET /admin/metrics?session_id=...&resource_id=...&provider_id=...
GET /admin/requests?limit=100&session_id=...&resource_id=...&provider_id=...
GET /admin/decisions?limit=100&session_id=...&resource_id=...&provider_id=...&request_id=...
GET /admin/routing
POST /admin/routing/reload
POST /admin/health/check

/admin/state returns Redis live state. /admin/events is an SSE stream of state snapshots and disables response buffering.

/admin/overview returns metrics, recent requests, and recent load-balancer decisions in one response. The dashboard uses it as the preferred history endpoint.

/admin/metrics returns:

{
"requests_last_minute": 0,
"active_sessions": 0,
"total_requests": 0,
"input_tokens": 0,
"output_tokens": 0,
"total_tokens": 0,
"telemetry_queue_depth": 0,
"series": []
}

Requests and decisions support provider_id filters. limit is clamped to 1..1000.

Rows include dashboard-facing fields such as provider_id, model_alias, status, token counts, and timestamps.

/admin/routing returns active in-process resources and registered model aliases. POST /admin/routing/reload reloads provider resources and aliases from SQL.

Proxy keys

GET /admin/keys
POST /admin/keys
DELETE /admin/keys/{key_id}

GET /admin/keys lists the calling org's proxy keys. Raw secrets are never returned.

POST /admin/keys mints a data-plane key and returns the full secret once:

{
"key": {
"id": "key_...",
"name": "ci-pipeline",
"prefix": "rprx-...",
"enabled": true
},
"secret": "rprx-..."
}

DELETE /admin/keys/{key_id} disables a key. Disabled keys no longer authenticate data-plane requests.

Provider registry

GET /admin/providers?include_disabled=false
GET /admin/provider-presets
POST /admin/providers
POST /admin/providers/test
GET /admin/providers/{provider_id}
PATCH /admin/providers/{provider_id}
DELETE /admin/providers/{provider_id}

GET /admin/providers lists providers and never returns raw credentials. Provider objects include adapter metadata such as adapter, adapter_supported, capabilities, credential_configured, credential_resolved, resolved_key_count, pool_size, and routable.

GET /admin/provider-presets returns frontend-friendly presets for OpenAI Responses, OpenAI-compatible Chat, Kimi, OpenRouter, and Anthropic.

POST /admin/providers/test validates unsaved provider settings and returns:

{
"ok": true,
"adapter": "chat_completions",
"message": "validated",
"capabilities": {}
}

Create provider example:

{
"id": "openai-main",
"name": "OpenAI Main",
"provider_type": "openai",
"base_url": "https://api.openai.com/v1",
"wire_api": "responses",
"auth_type": "bearer",
"api_key": "replace-with-provider-key"
}

Required create fields are id, name, and base_url. Defaults include provider_type="openai", wire_api="responses", auth_type="bearer", auth_header="authorization", health_path="models", and enabled=true.

In production, provider base_url must be HTTPS and public. Local HTTP or private-network provider URLs require explicit development flags on the backend. Custom auth_header values must be valid HTTP header names and cannot use reserved transport or client-credential headers such as cookie, host, content-length, or proxy-authorization.

Supported routable wire_api values are responses, chat_completions, and anthropic_messages. custom is accepted for registry storage but is not routable until an adapter exists.

Omitting api_key during PATCH preserves the existing inline credential. Use clear_api_key=true to remove it. DELETE /admin/providers/{provider_id} removes the provider row; the enabled field is for reversible runtime pauses while the row exists.

Provider credentials

GET /admin/providers/{provider_id}/credentials
POST /admin/providers/{provider_id}/credentials
POST /admin/providers/{provider_id}/credentials/test
GET /admin/providers/{provider_id}/credentials/{credential_id}
PATCH /admin/providers/{provider_id}/credentials/{credential_id}
DELETE /admin/providers/{provider_id}/credentials/{credential_id}
POST /admin/providers/{provider_id}/credentials/{credential_id}/test
POST /admin/providers/{provider_id}/credentials/{credential_id}/validate
POST /admin/providers/{provider_id}/credentials/{credential_id}/rotate

Credential rows are org-scoped and owned by one provider. They let the dashboard add, rotate, validate, disable, or delete BYOS credentials without editing model aliases. Raw secrets are never returned.

Supported credential kind values are api_key, bearer_token, external_ref, and feature-gated claude_code_oauth. A row can hold encrypted inline secret material or a credential_ref such as env:ZAI_API_KEY. If at least one enabled credential row resolves, routing uses those rows. If none exist, the provider falls back to env-sourced slots, provider-level encrypted api_key, and provider-level credential_ref.

Create credential example:

{
"label": "primary",
"kind": "api_key",
"secret": "replace-with-provider-key"
}

Credential responses include status fields such as credential_configured, secret_configured, last_validation_status, last_validation_error, expires_at, and needs_reauth.

Model and agent registry

GET /admin/models?include_disabled=false
POST /admin/models
POST /admin/models/test
GET|PATCH|DELETE /admin/models/{alias:path}

Aliases can contain slashes, such as openai/gpt-mini or openrouter/google/gemini. Dot-only path segments such as . or .. are rejected by the frontend BFF before an upstream call.

Create alias example:

{
"alias": "openai/gpt-mini",
"display_name": "GPT Mini",
"kind": "model",
"provider_id": "openai-main",
"upstream_id": "gpt-4.1-mini",
"capabilities": {
"tools": true,
"streaming": true,
"claude_code": {
"enabled": true,
"profile": "openai_responses",
"images": true,
"documents": true,
"client_tools": true,
"parallel_tools": true,
"hosted_tools": ["web_search"]
}
}
}

POST /admin/models/test validates a provider plus upstream model id:

{
"provider_id": "openai-main",
"upstream_id": "gpt-4.1-mini",
"input": "optional prompt",
"stream": false,
"surface": "claude_code",
"capabilities": {
"claude_code": {"profile": "openai_responses"}
}
}

surface defaults to responses. Use surface="claude_code" to validate Messages conversion and profile compatibility. This endpoint sends a text smoke request; it does not automatically probe declared image, document, or tool support.

capabilities.claude_code.profile accepts auto, anthropic_native, openai_responses, or openai_chat and must match the provider wire_api. Boolean capability fields default to true; hosted_tools defaults to [] and currently accepts only web_search. Chat Completions aliases must keep hosted_tools empty.

Allowed kind values are model, agent, and compound_agent.

DELETE /admin/models/{alias:path} removes the alias row. Historical telemetry keeps provider and alias strings for audit history.

Claude Code OAuth setup

POST /admin/claude-code/oauth/sessions
GET /admin/claude-code/oauth/sessions
GET /admin/claude-code/oauth/sessions/{session_id}
DELETE /admin/claude-code/oauth/sessions/{session_id}
POST /admin/claude-code/oauth/sessions/{session_id}/complete

Authenticated org members create setup sessions. The create response returns a one-time setup_token. The local responses-proxy-claude-code teleport command completes the session from a machine that is already logged in to Claude Code. Completion stores an encrypted claude_code_oauth credential row for the selected Anthropic provider.

The complete endpoint is intentionally not Morph-authenticated. It accepts only the one-time setup token in the request body; the token is stored as a hash, expires quickly, and cannot be replayed after completion or cancellation.

Persistence schema

SQL tables include:

  • service_providers
  • provider_credentials
  • registered_models
  • proxy_api_keys
  • resources
  • sessions
  • requests
  • load_balancer_decisions
  • response_states
  • telemetry_events
  • claude_code_oauth_sessions

The app runs Alembic migrations for managed schema changes and keeps lightweight startup compatibility checks for existing databases.