API Endpoints Reference
Complete reference for all Ozwell API endpoints.
Base URLs
| Environment | URL |
|---|---|
| Current Ozwell Manager API | https://ozwellapi.os.mieweb.org |
Use https://ozwellapi.os.mieweb.org for current Ozwell Manager features. Production is temporarily unavailable while it migrates to the future api.ozwell.ai endpoint, so it is not advertised as the active API here.
Chat
Create Chat Completion
Generate a response for a conversation.
POST /v1/chat/completions
Request Body
| Parameter | Type | Required | Description |
|---|---|---|---|
provider | string | No | Provider ID (e.g., openai, anthropic, ollama). Required when model is ambiguous across allowed providers. |
model | string | No | Model ID (e.g., gpt-4.1-mini). If omitted, uses the agent model-policy default, then the server-wide fallback, then LLM_MODEL — each still subject to the effective policy. |
messages | array | Yes | Array of message objects |
temperature | number | No | Sampling temperature (0-2). Default: 1 |
top_p | number | No | Nucleus sampling. Default: 1 |
n | integer | No | Number of completions to generate. Default: 1 |
stream | boolean | No | Stream responses. Default: false |
stop | string/array | No | Stop sequences |
max_tokens | integer | No | Maximum tokens to generate |
presence_penalty | number | No | Presence penalty (-2 to 2). Default: 0 |
frequency_penalty | number | No | Frequency penalty (-2 to 2). Default: 0 |
tools | array | No | List of tools (functions) available |
tool_choice | string/object | No | Tool selection behavior |
Message Object
{
"role": "user | assistant | system | tool",
"content": "Message content",
"name": "optional_name",
"tool_calls": [],
"tool_call_id": "for_tool_role"
}
Example Request
curl https://ozwellapi.os.mieweb.org/v1/chat/completions \
-H "Authorization: Bearer $OZWELL_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"provider": "openai",
"model": "gpt-4",
"messages": [
{"role": "system", "content": "You are a helpful assistant."},
{"role": "user", "content": "What is the capital of France?"}
],
"temperature": 0.7
}'
Response
{
"id": "chatcmpl-abc123",
"object": "chat.completion",
"created": 1699000000,
"model": "gpt-4",
"choices": [
{
"index": 0,
"message": {
"role": "assistant",
"content": "The capital of France is Paris."
},
"finish_reason": "stop"
}
],
"usage": {
"prompt_tokens": 25,
"completion_tokens": 10,
"total_tokens": 35
}
}
Embeddings
Create Embedding
Generate vector embeddings for text.
POST /v1/embeddings
Request Body
| Parameter | Type | Required | Description |
|---|---|---|---|
model | string | Yes | Model ID (e.g., text-embedding-ada-002) |
input | string/array | Yes | Text to embed |
encoding_format | string | No | float or base64. Default: float |
Example Request
curl https://ozwellapi.os.mieweb.org/v1/embeddings \
-H "Authorization: Bearer $OZWELL_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "text-embedding-ada-002",
"input": "The quick brown fox"
}'
Response
{
"object": "list",
"data": [
{
"object": "embedding",
"index": 0,
"embedding": [0.0023, -0.0092, 0.0156, ...]
}
],
"model": "text-embedding-ada-002",
"usage": {
"prompt_tokens": 4,
"total_tokens": 4
}
}
Files
Upload File
Upload a file for use with the API.
POST /v1/files
Request (multipart/form-data)
| Parameter | Type | Required | Description |
|---|---|---|---|
file | file | Yes | The file to upload |
purpose | string | Yes | Purpose: assistants, fine-tune, etc. |
Example Request
curl https://ozwellapi.os.mieweb.org/v1/files \
-H "Authorization: Bearer $OZWELL_API_KEY" \
-F "file=@document.pdf" \
-F "purpose=assistants"
Response
{
"id": "file-abc123",
"object": "file",
"bytes": 1024000,
"created_at": 1699000000,
"filename": "document.pdf",
"purpose": "assistants"
}
List Files
List all uploaded files.
GET /v1/files
Query Parameters
| Parameter | Type | Description |
|---|---|---|
purpose | string | Filter by purpose |
Response
{
"object": "list",
"data": [
{
"id": "file-abc123",
"object": "file",
"bytes": 1024000,
"created_at": 1699000000,
"filename": "document.pdf",
"purpose": "assistants"
}
]
}
Retrieve File
Get information about a specific file.
GET /v1/files/{file_id}
Delete File
Delete a file.
DELETE /v1/files/{file_id}
Retrieve File Content
Download file contents.
GET /v1/files/{file_id}/content
Audio
Create Transcription
Transcribes audio into the input language.
POST /v1/audio/transcriptions
This is a multipart/form-data endpoint.
Request Body (multipart/form-data)
| Parameter | Type | Required | Description |
|---|---|---|---|
file | file | Yes | The audio file to transcribe. Supported formats: mp3, mp4, mpeg, mpga, m4a, wav, webm |
model | string | Yes | Model ID (currently whisper-1) |
language | string | No | Language code in ISO-639-1 format |
response_format | string | No | Output format: json, text, srt, verbose_json, vtt. Default: json |
temperature | number | No | Sampling temperature (0-1). Default: 0 |
timestamp_granularities | array | No | Timestamp granularities: word, segment. Requires verbose_json format |
Example Request
curl https://ozwellapi.os.mieweb.org/v1/audio/transcriptions \
-H "Authorization: Bearer $OZWELL_API_KEY" \
-F "file=@audio.mp3" \
-F model=whisper-1
Response (json format)
{
"text": "Hello, this is a transcription of the audio file."
}
Response (verbose_json format)
{
"task": "transcribe",
"language": "english",
"duration": 3.0,
"text": "Hello, this is a transcription of the audio file.",
"words": [
{ "word": "Hello", "start": 0.0, "end": 0.5 }
],
"segments": [
{
"id": 0,
"seek": 0,
"start": 0.0,
"end": 3.0,
"text": "Hello, this is a transcription of the audio file.",
"tokens": [50364, 639, 307],
"temperature": 0.0,
"avg_logprob": -0.25,
"compression_ratio": 1.0,
"no_speech_prob": 0.01
}
]
}
Models
List Models
List discovered provider/model records. The registry is stored in the backend database and refreshed from the configured gateway/Ollama discovery paths.
GET /v1/models
Response (example — actual models depend on your backend configuration)
{
"object": "list",
"data": [
{
"id": "gpt-4.1-mini",
"provider": "openai",
"model": "gpt-4.1-mini",
"object": "model",
"created": 0,
"owned_by": "openai"
},
{
"id": "gpt-4o-mini",
"provider": "openai",
"model": "gpt-4o-mini",
"object": "model",
"created": 0,
"owned_by": "openai"
}
]
}
List Effective Models
List the provider/model records allowed for the current parent key or agent key.
GET /v1/models/effective
The effective list is:
enabled discovered models ∩ server-wide restrictions ∩ parent-key restrictions ∩ agent model policy
Each level only narrows the level above it, and an empty level is a no-op. Policy is resolved before
any provider dispatch, so a request for a disallowed pair is rejected with 403 model_not_allowed
before it reaches an upstream provider.
Retrieve Model
Get details about a specific model.
GET /v1/models/{model_id}
Manager Model Policy Endpoints
Manager-console routes expose the same provider-aware policy controls:
| Endpoint | Purpose |
|---|---|
GET /v1/manager/models | List/refresh discovered provider models for the manager console, narrowed by the server-wide policy |
GET /v1/manager/admin/model-restrictions | Read server-wide restrictions (admin only) |
PUT /v1/manager/admin/model-restrictions | Save server-wide restrictions with allowed_models (admin only) |
GET /v1/manager/admin/default-model | Read the server-wide fallback model (admin only) |
PUT /v1/manager/admin/default-model | Set or clear the server-wide fallback model (admin only) |
GET /v1/manager/admin/parent-keys/{key_id}/model-restrictions | Read parent-key restrictions |
PUT /v1/manager/admin/parent-keys/{key_id}/model-restrictions | Save parent-key restrictions with allowed_models |
GET /v1/manager/agents/{agent_id}/model-policy | Read an agent fallback model and allowed-model policy |
PUT /v1/manager/agents/{agent_id}/model-policy | Save an agent fallback model and allowed-model policy |
GET /v1/manager/notifications | List model-policy notifications |
Restriction bodies use provider-aware entries:
{
"allowed_models": [
{ "provider": "openai", "model": "gpt-4o-mini" },
{ "provider": "anthropic" }
]
}
An empty allowed_models array means unrestricted within the higher-level effective policy.
An entry with a provider and no model allows that whole provider.
Server-Wide Restrictions
GET/PUT /v1/manager/admin/model-restrictions set one allow-list for the entire server. They
require an admin manager user and return 403 admin_required otherwise. The policy applies to every
key and agent, including requests made with no parent key, and takes effect on the next request with
no restart.
{
"allowed_models": [{ "provider": "ollama", "model": "gemma3:1b" }],
"discovered_models": [],
"effective_models": []
}
discovered_models is the unfiltered registry, so an admin UI can still offer every discovered model
to choose from; effective_models is the same list after the server-wide policy is applied.
Server-wide policy is stored in its own table and never touches provider_models.enabled, which
discovery refresh owns. Saving it does not modify or delete any parent-key or agent policy — those
stay stored exactly as written and simply narrow further.
If a save takes a model away, every key that loses one gets a model_policy_changed notification —
the same one a per-key change sends. Keys that lose nothing are not notified.
A save that would exclude the server-wide fallback model is rejected with 400
default_model_not_allowed, since it would leave every request that names no model with nothing to
resolve to. Change the fallback first, or keep its model approved.
Server-Wide Fallback Model
GET/PUT /v1/manager/admin/default-model set the model used when a request names none. Admin only,
403 admin_required otherwise. A stored value beats LLM_MODEL and every other environment value,
and is read per request, so a save takes effect without a restart.
{
"provider": "openai",
"model": "gpt-4o-mini"
}
Sending both fields as null clears the fallback and drops the server back to its environment chain:
agent model-policy default → server-wide fallback → environment → gpt-4o-mini
The environment step is the environment_model below — LLM_MODEL when LLM_BASE_URL is set, the
first Ollama model when it is not and Ollama is reachable, otherwise DEFAULT_MODEL.
Both endpoints return the stored value plus effective_models, the list an admin picker should offer:
{
"default_model": { "provider": "openai", "model": "gpt-4o-mini" },
"environment_model": { "provider": "openai", "model": "gpt-4o-mini" },
"effective_models": []
}
environment_model is what the server falls back to when default_model is null, resolved from the
backend in use — LLM_MODEL, the first Ollama model, or DEFAULT_MODEL. It is reported so an admin
console can show the model actually in use rather than an empty control.
A PUT naming a model the server cannot serve is rejected with 400 default_model_not_allowed —
whether it is missing from the registry, blocked by the server-wide allow-list, or available only under a different provider. Chat resolves the
fallback through the same effective list, so a model accepted here but absent there would return 403
model_not_allowed on the next request that names no model.
The check runs whenever the registry holds anything. A fresh server whose registry is still empty accepts any pair, because there is nothing to check against and the stored value seeds the registry itself.
Only the server-wide allow-list is guarded this way. A parent-key or agent restriction that excludes
the fallback is still accepted, and a request from that key naming no model then fails with 403
model_not_allowed. That is deliberate — a key narrowed to one provider should not be forced to keep
a fallback from another — but it means a narrowed key needs its own default model set through
PUT /v1/manager/agents/{agent_id}/model-policy.
A PUT naming only one of provider and model is rejected with 400 invalid_default_model. Send
both to set a fallback, or both as null to clear it.
The same value seeds the model registry when discovery returns nothing, replacing LLM_MODEL and
LLM_PROVIDER in that path too.
Responses (Ozwell Extension)
Extended response format with additional capabilities.
Create Response
Create a response with extended features.
POST /v1/responses
Request Body
Includes all chat/completions parameters plus:
| Parameter | Type | Description |
|---|---|---|
conversation_id | string | ID for conversation persistence |
include_sources | boolean | Include source citations |
response_format | object | Structured output format |
Example Request
curl https://ozwellapi.os.mieweb.org/v1/responses \
-H "Authorization: Bearer $OZWELL_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "gpt-4",
"messages": [{"role": "user", "content": "Summarize this document"}],
"conversation_id": "conv_abc123",
"include_sources": true
}'
Response
{
"id": "resp-abc123",
"object": "response",
"created": 1699000000,
"model": "gpt-4",
"conversation_id": "conv_abc123",
"output": {
"role": "assistant",
"content": "Here is the summary..."
},
"sources": [
{
"file_id": "file-xyz789",
"filename": "document.pdf",
"page": 3,
"excerpt": "Relevant excerpt..."
}
],
"usage": {
"prompt_tokens": 150,
"completion_tokens": 80,
"total_tokens": 230
}
}
Pagination
List endpoints support pagination:
| Parameter | Type | Description |
|---|---|---|
limit | integer | Max items to return (1-100). Default: 20 |
after | string | Cursor for next page |
before | string | Cursor for previous page |
Example
# First page
curl "https://ozwellapi.os.mieweb.org/v1/files?limit=10" \
-H "Authorization: Bearer $OZWELL_API_KEY"
# Next page
curl "https://ozwellapi.os.mieweb.org/v1/files?limit=10&after=file-abc123" \
-H "Authorization: Bearer $OZWELL_API_KEY"
Widget Sign-In
Sign-in routes for the embeddable widget. They mint a sess_ token that stands in for the
signed-in user's own API key — see Authentication.
These routes take no API key. Everything else on this page does.
List Sign-In Methods
Report which methods this server offers, so the widget only shows what will work.
GET /auth/methods
{ "google": true, "apple": true, "email_otp": true, "user_key": true }
google is false unless the server has GOOGLE_CLIENT_ID and GOOGLE_CLIENT_SECRET set.
apple requires all four Apple settings described in Widget Authentication.
Request an Email Code
POST /auth/otp/request
{ "email": "user@example.com" }
Returns a challenge_id. The code is six digits, lasts 10 minutes, and allows 5 attempts.
One address may request 3 codes per 15 minutes; beyond that the route returns 429. A
malformed address returns 400, and 502 means the code could not be delivered.
Where the code goes depends on SMTP_URL. With it set, the code is mailed and kept out of the
logs. Without it — the normal local-development case — the code is logged instead, and
AUTH_DEV_ECHO_OTP=1 additionally returns it as dev_code. That echo is ignored whenever a
mail sender is configured, so it cannot bypass delivery.
Verify an Email Code
POST /auth/otp/verify
{ "challenge_id": "...", "code": "123456" }
Returns { "session_token": "sess_...", "email": "..." }. A wrong or expired code returns
401. Each challenge verifies once.
Start Google Sign-In
GET /auth/oidc/google/start
Redirects to Google's consent screen. OAuth 2.0 authorization code with PKCE (S256), plus
state and nonce. Returns 404 when Google is not configured.
Both providers share a limit of 1,000 pending sign-in flows per process; new starts
return 429 at capacity until flows complete or expire after ten minutes.
Open this in a popup, not in an iframe — Google refuses to render consent in a frame.
Google Callback
GET /auth/oidc/google/callback
Google's redirect target. Exchanges the code, verifies the ID token against Google's JWKS
(issuer, audience, nonce and email_verified), then mints a session. Responds with a small page
that posts { source: 'ozwell-auth', session_token, email } to window.opener and closes
itself.
Register this URL under Authorized redirect URIs in Google Cloud Console — not under
Authorized JavaScript origins, which rejects any URL carrying a path. The server builds it from
PUBLIC_BASE_URL, never from request input.
Start Apple Sign-In
GET /auth/oidc/apple/start
Opens Apple consent in a popup using authorization code flow, state, nonce,
and response_mode=form_post. Returns 404 when Apple is not configured.
Apple Callback
POST /auth/oidc/apple/callback
Content-Type: application/x-www-form-urlencoded
Accepts Apple's code and state (or provider error), exchanges the code using an
ES256 client secret, and verifies the signed identity token and verified email.
Returns the same origin-restricted popup response as Google. Register the exact
public HTTPS return URL built from PUBLIC_BASE_URL; localhost is not supported.
See Widget Authentication for Apple configuration.
Describe the Current Session
GET /auth/session
With Authorization: Bearer sess_..., returns { "email": "...", "user_id": ... }. An expired
or unknown token returns 401.
Sign Out
POST /auth/logout
Revokes the token in the Authorization header. Sessions otherwise expire 24 hours after
sign-in, and are lost on server restart.
Versioning
The API is versioned in the URL path (/v1/). Breaking changes will result in a new version.
Current version: v1
Agents
Agent registration and management API. See the full reference: