# API Documentation

OpenAI-compatible inference with one API key.

> Point any OpenAI-compatible client at `https://logfare.ai/v1` and use your Logfare API key. Anthropic Messages and the OpenAI Responses API also work. Model IDs change often, so check [/models](/models) for what's available right now.

## Authentication

Model requests need your API key. [Register](/register) for a free account and copy the key from your dashboard. Your Discord account must stay in the Logfare server to keep access.

Send the key as a Bearer token. The `x-api-key` header also works, which is what Anthropic clients send.

```http
Authorization: Bearer YOUR_API_KEY
```

Building an app for other people? Use [App integrations](#app-integrations) so each user approves your app without sharing their key.

## Endpoints

All endpoints are under `https://logfare.ai/v1`.

| Method | Endpoint | Description |
| --- | --- | --- |
| **POST** | `/v1/chat/completions` | Chat completions (streaming supported) |
| **POST** | `/v1/responses` | OpenAI Responses API |
| **POST** | `/v1/messages` | Anthropic Messages API |
| **POST** | `/v1/completions` | Legacy text completions |
| **POST** | `/v1/images/generations` | Image generation |
| **POST** | `/v1/images/edits` | Image editing |
| **POST** | `/v1/audio/speech` | Text to speech |
| **POST** | `/v1/audio/transcriptions` | Speech to text |
| **POST** | `/v1/embeddings` | Text embeddings |
| **POST** | `/v1/videos/generations` | Video generation |
| **POST** | `/v1/decisions` | Decision models |
| **GET** | `/v1/models` | Available models (no key needed) |
| **GET** | `/v1/status` | Service and model health (no key needed) |

## Models & access

**GET** `/v1/models`

Lists the models you can use, in the OpenAI format. No key is needed; with a key, the list also includes private models your key can reach. Each entry adds these Logfare fields:

| Field | Description |
| --- | --- |
| `endpoints` | APIs the model serves, such as `chat/completions` or `images/generations` |
| `aliases` | Other names that route to the same model |
| `requires_training_optin` | `true` for premium models (see below) |
| `premium_unlocked` | Whether the key you sent can use the model right now |
| `chat_capabilities` | Input types (`input_modalities`), `reasoning` controls, and `tools` support for chat models |
| `context_length`, `max_output_tokens` | Input and output limits Logfare enforces for the model, when set. Larger inputs are rejected with `413`; larger `max_tokens` values are lowered. |
| `upstream_metadata` | Published specs such as context length, input types and supported parameters, when available. They are a general reference, so check the limits above first. |

**Premium models** are free too, but require opting in to model training at [/consent](/consent). Until you opt in, requests to them return `403` with code `training_optin_required`. Every other model works without opting in.

```bash
curl https://logfare.ai/v1/models
```

## Chat Completions

**POST** `/v1/chat/completions`

Standard OpenAI chat completions. Set `"stream": true` to stream over SSE.

| Parameter | Type | Required | Description |
| --- | --- | --- | --- |
| `model` | string | Yes | Model ID from [/models](/models), or `logfare/auto` |
| `messages` | array | Yes | Array of `{role, content}` messages |
| `stream` | boolean | No | Stream the response over SSE |
| `max_tokens` | integer | No | Maximum tokens to generate |
| `temperature`, `top_p`, `stop` | | No | Standard sampling options |

Other OpenAI parameters, such as `tools`, `response_format`, `reasoning_effort`, and image content parts, are passed to the model's provider. Check the model's `chat_capabilities` to see what it supports.

```bash
curl https://logfare.ai/v1/chat/completions \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -d '{
  "model": "logfare/auto",
  "messages": [
    {"role": "user", "content": "Hello!"}
  ]
}'
```

```python
from openai import OpenAI

client = OpenAI(
    base_url="https://logfare.ai/v1",
    api_key="YOUR_API_KEY",
)

stream = client.chat.completions.create(
    model="logfare/auto",
    messages=[{"role": "user", "content": "Tell me a story"}],
    stream=True,
)

for chunk in stream:
    if chunk.choices and chunk.choices[0].delta.content:
        print(chunk.choices[0].delta.content, end="")
```

## Auto Routing

Use `logfare/auto` as the model and Logfare picks the best available chat model for you. If a model fails, the next one in the chain is tried, so you still get a response. `auto` and `logfare/random` are aliases.

Routing only uses models your key can access. It works with Chat Completions, Messages, and Responses.

## Messages & Responses

**POST** `/v1/messages` &nbsp; **POST** `/v1/responses` &nbsp; **POST** `/v1/completions`

Use the Anthropic Messages format, the OpenAI Responses format, or legacy completions with the same models and key. Point an Anthropic SDK's base URL at `https://logfare.ai` (without `/v1`).

```bash
curl https://logfare.ai/v1/messages \
  -H "Content-Type: application/json" \
  -H "x-api-key: YOUR_API_KEY" \
  -H "anthropic-version: 2023-06-01" \
  -d '{
  "model": "logfare/auto",
  "max_tokens": 1024,
  "messages": [
    {"role": "user", "content": "Hello!"}
  ]
}'
```

## Images

**POST** `/v1/images/generations`

| Parameter | Type | Required | Description |
| --- | --- | --- | --- |
| `model` | string | Yes | Image model ID |
| `prompt` | string | Yes | Description of the image |
| `size` | string | No | Dimensions as `WxH`, such as `1024x768` |
| `n`, `steps`, `response_format` | | No | Supported by some models |

**POST** `/v1/images/edits` takes the same fields as `multipart/form-data`, plus the source `image`. Only models listing `images/edits` support it.

```bash
curl https://logfare.ai/v1/images/generations \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -d '{
  "model": "flux-2-dev",
  "prompt": "a cat sitting on a windowsill, warm afternoon light",
  "size": "1024x1024"
}'
```

## Text to Speech

**POST** `/v1/audio/speech`

Send `model` and the `input` text as JSON; `voice` is optional and depends on the model. Returns audio.

## Transcriptions

**POST** `/v1/audio/transcriptions`

Send `model` and the audio `file` as `multipart/form-data`. Returns the transcribed text.

## Embeddings

**POST** `/v1/embeddings`

Send `model` and `input`, a string or an array of strings. Returns one vector per input.

## Video

**POST** `/v1/videos/generations`

Send `model` and `prompt` as JSON. The request stays open until the video is ready, which can take several minutes, so set a long client timeout. Other fields depend on the model.

## Decision Models

**POST** `/v1/decisions`

A decision model reads a `state` and a set of typed questions, and returns a probability for every allowed answer instead of generating text. Use it to classify, route, or score. Models: `clef` (27B, most precise), `clef-flash` (9B, faster), and `8ball`, a joke model that answers by hashing the state (deterministic and not logged). Questions about self-harm always get a supportive no, marked `"support": true`.

| Parameter | Type | Required | Description |
| --- | --- | --- | --- |
| `model` | string | Yes | `clef`, `clef-flash`, or `8ball` |
| `state` | string \| object | Yes | What to decide on: text, JSON, or an image or video reference |
| `questions` | object | No | Map of question name to `{type, instructions, criteria}`. Omit it for a single yes/no answer about the whole state. |

Question types: `noul` returns a yes/no probability. `choice` takes a map of options to descriptions and returns the chosen option with per-option probabilities. `score` takes an ordered list of levels and returns a weighted score with per-level probabilities.

```bash
curl https://logfare.ai/v1/decisions \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -d '{
  "model": "clef",
  "state": "Checkout has been failing for every customer for the last hour.",
  "questions": {
    "urgent": { "type": "noul", "instructions": "Is this support request urgent?" },
    "team": {
      "type": "choice",
      "instructions": "Which team should handle this request?",
      "criteria": {
        "billing": "Payments, invoices, and refunds",
        "technical": "Outages, errors, and configuration",
        "sales": "Plans and upgrades"
      }
    },
    "severity": {
      "type": "score",
      "instructions": "How severe is the customer impact?",
      "criteria": ["No impact", "Minor", "Major", "Critical"]
    }
  }
}'
```

Response (`answers` is keyed by question name):

```json
{
  "result": {
    "model": "clef",
    "answers": {
      "urgent": { "type": "noul", "noul": 0.9895 },
      "team": {
        "type": "choice",
        "choice": "technical",
        "probabilities": { "billing": 0.1584, "technical": 0.8274, "sales": 0.0142 },
        "confidence": 0.5648
      },
      "severity": {
        "type": "score",
        "score": 2.9484,
        "legend": { "0": "No impact", "1": "Minor", "2": "Major", "3": "Critical" },
        "probabilities": { "0": 0.0043, "1": 0.0047, "2": 0.0292, "3": 0.9618 },
        "confidence": 0.9012
      }
    }
  },
  "success": true,
  "errors": [],
  "messages": []
}
```

## Request limits

These are the current limits and update automatically when they change.

| Scope | Request budget | Concurrent requests |
| --- | --- | --- |
| Regular account | 20 requests per minute<br>500 requests per hour<br>2,500 requests per day | 3 |
| New account | 5 requests per minute<br>100 requests per hour<br>250 requests per day | 1 |
| Shared network / IP | 60 requests per minute<br>3,000 requests per hour<br>25,000 requests per day | Account and service limits apply |

New accounts use the new-account budget for their first 24 hours.

Up to 64 requests can be in progress across all accounts. Individual models and providers can have their own shared limits, so capacity isn't guaranteed.

Limits apply across all endpoints and use fixed UTC windows; provider daily budgets reset at 00:00 UTC. Regenerating your key doesn't reset them. When you hit a limit you get a `429`: wait for the `Retry-After` time instead of retrying immediately.

## Errors

Errors use the OpenAI format. `/v1/messages` and `/v1/responses` use the Anthropic and Responses formats instead.

```json
{
  "error": {
    "message": "Your account has reached its request budget. Try again later.",
    "type": "rate_limit_error"
  }
}
```

| Status | Meaning |
| --- | --- |
| 400 | Invalid request. Code `content_policy_violation` means content moderation blocked it. |
| 401 | Missing or invalid API key |
| 403 | No access: Discord membership or screening needed, a private model, or a premium model that needs training opt-in (code `training_optin_required`) |
| 404 | Unknown model, or the model doesn't support this endpoint |
| 413 | Request body too large, or input over the model's context limit (code `context_length_exceeded`) |
| 429 | Request limit reached. Respect `Retry-After`. |
| 5xx | The provider failed or the service is busy. Retry with backoff or try another model. |

Errors returned by a model's provider are passed through with the provider's status code and message.

## Account API

These endpoints power the Logfare website and use its login session, not your API key. Create an account on [the registration page](/register). Existing accounts without Discord can link it at [/migrate](/migrate). Each Discord account links to one Logfare account, and leaving the server suspends access until you rejoin.

> **For third-party apps, use App integrations.** A browser session isn't a safe credential to copy into another app. A registered app gets only the permissions you approve and its own revocable credential. See [App integrations](#app-integrations).

| Method | Endpoint | Description |
| --- | --- | --- |
| **GET** | `/v1/auth/me` | Current account |
| **POST** | `/v1/auth/regenerate-key` | Replace your API key |
| **POST** | `/v1/auth/training-preference` | Opt in to or out of model training (`{"training_opt_in": true}`), which unlocks premium models |
| **POST** | `/v1/auth/eval-preference` | Opt out of evaluation datasets (`{"eval_dataset_opt_out": true}`) |
| **POST** | `/v1/auth/discord/membership/check` | Refresh access after rejoining the Discord server |
| **POST** | `/v1/me/delete` | Delete your account after a 30-day recovery period |

## App integrations

Users can grant an app specific access without sharing their password, browser session, or main API key. They can revoke its separate credential at any time.

### Register your application

In [Developer Apps](/dashboard#developer-apps), create an app and choose its permissions. Its public `lfa_…` app ID identifies the registration. Each account can have five apps; deleting one frees a slot.

**Who can connect?** Choose **Anyone** or **Only you**. Only you restricts approval to the creator's Logfare account. Switching to it revokes other users' connections; switching back permits new approvals without restoring revoked tokens.

**App authentication** defaults to **App ID only**, for distributed apps and plugins. Anyone knowing the ID can start a request, but account access still requires user approval. For backends or private automation, choose **Require app secret**: requests must also authenticate with the generated secret. Copy it when shown; Logfare stores only its hash. Keep it out of distributed apps, where it could be extracted.

Changing authentication or resetting a secret cancels unfinished connection attempts. Existing approved connections stay active. An app ID alone does not prove which executable is running; confirm that you started the connection in an app you trust.

### Select permissions

Each connection can request a subset of the permissions you register. Users see and approve those exact permissions. Scopes are read-only for account data; none can change privacy choices, API keys, or account settings.

- **Chat and coding** (`inference:chat`): Send text-model requests and receive responses, including streaming and tool calls.
- **Images** (`inference:images`): Generate and edit images using your account's available models.
- **Audio** (`inference:audio`): Generate speech and transcribe audio using your account's available models.
- **Video** (`inference:video`): Generate videos using your account's available models.
- **Embeddings** (`inference:embeddings`): Create embeddings using your account's available models.
- **Read account usage** (`usage:read`): View aggregate account-wide usage: totals, time charts, and breakdowns by model and API/app connection. No prompts, responses, or individual request history.
- **Read privacy preferences** (`consent:read`): View training and evaluation preferences, including when each was last changed. Cannot change either preference.
- **Read basic account info** (`account:read`): View your Logfare user ID, username, and account creation date. No API key or account changes.
- **Read Discord account and server status** (`discord:read`): View your linked Discord user ID, whether you are in the Logfare server, and when you joined. This status remains readable after leaving the server while your Logfare account is active. No Discord token, role list, or Discord account access.

`inference:chat` covers chat, completions, Anthropic Messages, and Responses. Images covers generation and edits; audio covers speech and transcription. Scopes do not bypass account access, training requirements, or shared limits. `/v1/models` is public.

### Connect an account

Secret-required apps authenticate **both requests below** using HTTP Basic auth: the app ID as username and app secret as password. Form-encoded `client_secret` with `client_id` is also supported; use one authentication method. Never put a secret in a URL. App ID only clients omit the secret.

1. Request a device code with your app ID and registered scopes.
2. Show the user code and open `verification_uri_complete`; also offer the manual `verification_uri`. The user signs in, verifies Discord if prompted, then approves or denies.
3. Poll at the returned interval using the temporary `device_code`.
4. Store the access token in the host's secure credential manager and send it as a Bearer token.

```http
POST https://logfare.ai/v1/integrations/oauth/device_authorization
Content-Type: application/x-www-form-urlencoded

client_id=lfa_YOUR_APP_ID&scope=inference%3Achat%20consent%3Aread
```

This requests chat and read-only privacy preferences; register both first. If omitted, `scope` defaults to registered `inference:chat`. Unknown or unregistered scopes return `invalid_scope`.

The response includes `device_code`, `user_code`, `verification_uri`, `verification_uri_complete`, `expires_in=900` (15 minutes), and `interval=5` (seconds).

```http
POST https://logfare.ai/v1/integrations/oauth/token
Content-Type: application/x-www-form-urlencoded

grant_type=urn:ietf:params:oauth:grant-type:device_code&client_id=lfa_YOUR_APP_ID&device_code=DEVICE_CODE
```

Success returns a Bearer access token, granted scopes, and a 180-day expiry. Device codes are single-use; there is no refresh token, so reconnect after expiry or revocation.

### Waiting, errors, and recovery

- `authorization_pending`: keep polling; `slow_down`: add at least five seconds to the interval.
- For HTTP 429, honor `Retry-After`. Back off on network errors or HTTP 503, and let the user cancel.
- `access_denied`: stop. For `expired_token` or `invalid_grant`, offer a new connection.
- `invalid_client`: check the app ID, status, and required secret. For `invalid_scope`, check registered permissions. On API 401, clear the stale token; on `insufficient_scope`, request new approval.

### Read approved account information

| GET endpoint | Required scope | Response |
| --- | --- | --- |
| `/v1/me/usage?hours=168` | `usage:read` | Account-wide totals, zero-filled time buckets, model and API/app breakdowns, last API use, and generation time. Windows: 24, 168 (default), or 720 hours. Cached for 60 seconds after checking access. |
| `/v1/me/consent` | `consent:read` | `training_opt_in`, `eval_dataset_opt_out`, and their respective `training_pref_updated_at` and `eval_pref_updated_at` timestamps (null if never changed). |
| `/v1/me/profile` | `account:read` | `user_id`, `username`, and UTC `created_at`. |
| `/v1/me/discord` | `discord:read` | `discord_user_id` as a string or null, `discord_is_member`, and `discord_joined_at` (null if unlinked or unavailable). No Discord access token or role list. |

```http
GET https://logfare.ai/v1/me/consent
Authorization: Bearer <access_token>

{"training_opt_in": false, "eval_dataset_opt_out": false}
```

For model selection, hide models marked `requires_training_optin=true` when `training_opt_in=false`. Evaluation preference is independent and does not unlock models. Logfare still checks current access on every inference request.

The account owner's browser session also works, but a Bearer token needs its own scope. These read-only endpoints are not a Sign in with Logfare service: browser callbacks and identity tokens are not provided. Deactivation revokes connections. Leaving the Logfare Discord server blocks inference and account reads until access is restored; `discord:read` can still report membership for an active account.

### Permissions and connection lifecycle

Changing an app's permissions never silently expands existing credentials. Removing a scope removes it from credentials and invalidates pending requests that need it. Users can revoke connections individually; disabling or deleting an app revokes all of them. Revoked credentials stay revoked, while usage attribution is retained.

Developers see connection totals, not users' credentials, prompts, responses, or request history. Account data requires a user's approved token with the matching scope.

### Connect and make a test request

Run your integration's normal setup or connect command, approve the requested permissions in Logfare, then select a Logfare model in the integration. For an OpenAI-compatible client, use this configuration:

```text
Base URL: https://logfare.ai/v1
API credential: the access token returned after approval
Model: logfare/auto
```

Keep `/v1` exactly once in the base URL. Test with a short chat request, then review the connection and usage in your dashboard. Revoke test connections you no longer need.

Use HTTPS outside loopback development. Never log device codes or access tokens or put them in URLs. The protocol follows the [OAuth device authorization grant](https://www.rfc-editor.org/rfc/rfc8628.html).

## Help & community

For questions, bug reports, feature ideas and model requests, or to talk with other users, join [r/Logfare](https://www.reddit.com/r/Logfare/) or the [Logfare Discord](https://discord.gg/QvuwEPNzDj).

Never post API keys, access tokens or private details publicly. For problems with your own account, open a support ticket in the Discord instead of posting on Reddit.
