Skip to content

Chat Completions

POST https://api.bve.me/v1/chat/completions

Requires Authorization: Bearer sk-bve-YOUR_KEY.

This endpoint proxies directly to Fuelix /chat/completions. The request body and response shape follow the OpenAI Chat Completions API. Streaming (SSE) is supported.

{
"model": "gpt-4o",
"messages": [
{ "role": "system", "content": "You are a helpful assistant." },
{ "role": "user", "content": "What is 2 + 2?" }
],
"stream": false
}
FieldTypeRequiredDescription
modelstringYesModel ID (e.g. gpt-4o)
messagesarrayYesArray of message objects ({role, content})
streambooleanNoEnable SSE streaming
temperaturenumberNoSampling temperature — [0, 2]
top_pnumberNoNucleus sampling — [0, 1]
max_tokensintegerNoMaximum tokens to generate (positive integer)
max_completion_tokensintegerNoNewer alias for max_tokens; preferred for o-series models (positive integer)
nintegerNoNumber of completions to return (positive integer)
stopstring | arrayNoStop sequences — string or array of ≤ 4 strings
presence_penaltynumberNoPresence penalty — [-2, 2]
frequency_penaltynumberNoFrequency penalty — [-2, 2]
logit_biasobjectNoToken bias map — object of token ID strings to numbers in [-100, 100]
toolsarrayNoFunction tool definitions for function calling (non-empty; see Function calling)
tool_choicestring | objectNoTool selection — "none", "auto", "required", or {type:"function", function:{name}}
response_formatobjectNoOutput format — {type: "text"}, {type: "json_object"}, or {type: "json_schema", json_schema: {name, schema}}
stream_optionsobjectNoSSE streaming options — {include_usage: boolean}; requires stream: true
logprobsbooleanNoReturn log probabilities of output tokens (OpenAI, Groq, OpenRouter; use with top_logprobs)
top_logprobsintegerNoNumber of top-token log probabilities per output token — [0, 20]; requires logprobs: true
parallel_tool_callsbooleanNoAllow the model to call multiple tools simultaneously (default true for OpenAI, Groq, OpenRouter)
userstringNoEnd-user identifier — forwarded to Fuelix for abuse detection (≤ 256 chars)
seedintegerNoDeterministic seed — must be an integer (not a float)
storebooleanNoPersist the response for OpenAI model distillation or evals — must be a boolean (true or false); silently ignored by non-OpenAI providers
service_tierstringNoCompute tier for the request: "auto", "default", "flex", or "scale" (OpenAI and OpenRouter; forwarded unchanged)
reasoning_effortstringNoReasoning budget for o-series models: "low", "medium", "high", or "auto" (see Reasoning models)
max_reasoning_tokensintegerNoo-series reasoning token budget — sets an upper bound on internal reasoning tokens; 0 disables extended thinking, positive values cap it (o3, o3-mini, o4-mini)
thinkingobjectNoClaude extended thinking configuration — { "type": "enabled", "budget_tokens": N } (N ≥ 1024) or { "type": "disabled" }. Enables extended reasoning traces for claude-3-7-sonnet and later. See Claude extended thinking
modalitiesarrayNoOutput modalities to request — non-empty array of "text" and/or "audio". Omit for text-only output. Include "audio" with an audio config object to receive spoken audio (OpenAI audio models only)
audioobjectNoAudio output configuration for gpt-4o-audio-preview and similar audio-capable models. Required when modalities includes "audio". Fields: voice (required — one of alloy, ash, ballad, cedar, coral, echo, fable, marin, nova, onyx, sage, shimmer, verse) and format (optional — one of aac, flac, mp3, opus, pcm16, pcm24, wav)
top_kintegerNoTop-k sampling — restricts the token pool to the k most likely next tokens; must be ≥ 1 (Cohere command-*, Groq, OpenRouter)
min_pnumberNoMinimum probability threshold for token sampling — [0, 1]; a token is only sampled if its probability is ≥ min_p × (max token probability) (OpenRouter extension)
top_anumberNoTop-a sampling — [0, 1]; a token is only considered if its probability is ≥ top_a × (max token probability)²; complements top_p and min_p (OpenRouter extension)
repetition_penaltynumberNoMultiplicative penalty applied to already-seen tokens — must be > 0 (values > 1 reduce repetition, values < 1 encourage it); distinct from the additive presence_penalty/frequency_penalty (Cohere, Mistral, OpenRouter)
thinking_configobjectNoGemini 2.5 thinking budget configuration — { "thinking_budget": N } where N is a non-negative integer; 0 disables thinking, positive values set the token budget (Gemini 2.5 Flash and Pro via OpenRouter)
transformsarrayNoOpenRouter prompt-transformation pipeline — array of transformation name strings (e.g. ["middle-out"]). Applied by OpenRouter before inference; forwarded unchanged and silently ignored by non-OpenRouter providers. See OpenRouter prompt transforms
providerobjectNoOpenRouter provider routing object — controls upstream provider selection, fallback behavior, and data collection. See OpenRouter provider routing
predictionobjectNoPredicted Outputs — supply expected output text to accelerate generation via speculative decoding. Object with type: "content" (required) and content (required): a string or array of {type: "text", text: string} blocks. See Predicted Outputs
functionsarrayNoDeprecated — legacy function definitions from the pre-tools API (OpenAI SDK v0.x). Each entry must have a name matching [a-zA-Z0-9_-]{1,64}. Prefer tools for new integrations.
function_callstring | objectNoDeprecated — legacy function routing from the pre-tool_choice API. "none", "auto", or {name: "<function-name>"}. Prefer tool_choice for new integrations.
web_search_optionsobjectNoWeb search configuration for search-capable OpenAI models (gpt-4o-search-preview, gpt-4.1, etc.). See Web search

Any additional fields supported by Fuelix are forwarded as-is.

BVE Gateway adds the following headers to every allowed (non-429) response:

HeaderExampleDescription
X-Request-Id550e8400-…UUID for this request (generated per request)
X-BVE-Client-Idmy-trace-123Echo of the client-supplied X-Request-Id (when present and valid: alphanumeric + -_., ≤ 128 chars). Absent when the client did not send X-Request-Id or the value failed validation.
X-BVE-Latency143Total gateway latency in milliseconds
X-BVE-Modelgpt-4oModel ID resolved for this request (from response JSON when buffered, from request body for streaming)
X-BVE-Key-Nameprod-keyName of the API key used for this request (redacted if it matches a provider credential pattern)
X-RateLimit-Limit-Requests60Per-minute request cap for this key
X-RateLimit-Remaining-Requests57Requests remaining in the current minute window
X-RateLimit-Reset-Requests42sSeconds until the minute window resets
X-RateLimit-Limit-Day10000Per-day request cap for this key
X-RateLimit-Remaining-Day9843Requests remaining until UTC midnight
X-RateLimit-Reset-Day38412sSeconds until the next UTC midnight
X-RateLimit-Limit-Month1000Monthly request cap (only set when monthly_limit is configured)
X-RateLimit-Remaining-Month748Requests remaining before the monthly request cap (only set when configured)
X-RateLimit-Reset-Month604800sSeconds until the start of the next UTC calendar month (only set when configured)
X-RateLimit-Limit-Tokens50000Monthly token cap (only set when monthly_token_limit is configured)
X-RateLimit-Remaining-Tokens47832Tokens remaining before the monthly token cap (only set when configured)
X-RateLimit-Reset-Tokens691200sSeconds until the monthly token window resets (only set when configured)

All X-RateLimit-* headers reflect the per-key limits configured in BVE Gateway and are exposed via CORS. See Rate Limits & Quotas for full details and example output.

The following Fuelix upstream headers are forwarded to the client when present:

HeaderDescription
content-typeResponse content type
content-lengthResponse body size
cache-controlCache directives
x-request-idFuelix’s own request ID
x-quota-allowed, x-quota-available, x-quota-resetFuelix quota headers
x-ratelimit-limit-requests, x-ratelimit-limit-tokensUpstream request/token limits
x-ratelimit-remaining-requests, x-ratelimit-remaining-tokensUpstream remaining capacity
x-ratelimit-reset-requests, x-ratelimit-reset-tokensUpstream window reset times
retry-afterSeconds until the rate-limit window resets (present on 429/503)
anthropic-ratelimit-requests-limit, anthropic-ratelimit-requests-remaining, anthropic-ratelimit-requests-resetAnthropic Messages API request limits (present on /v1/messages responses)
anthropic-ratelimit-tokens-limit, anthropic-ratelimit-tokens-remaining, anthropic-ratelimit-tokens-resetAnthropic Messages API token limits (present on /v1/messages responses)
x-groq-request-idGroq internal request ID for log correlation
openai-processing-msOpenAI server-side processing time
x-openrouter-modelActual model ID selected by OpenRouter after provider routing (e.g. openai/gpt-4o)
x-or-cache-statusOpenRouter semantic cache result: HIT or MISS
x-or-remaining-tokensToken budget remaining in the OpenRouter rate-limit window

All other Fuelix upstream headers are stripped. See Security Notes for the full list.

Terminal window
curl https://api.bve.me/v1/chat/completions \
-H "Authorization: Bearer sk-bve-YOUR_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "gpt-4o",
"messages": [{ "role": "user", "content": "What is 2 + 2?" }]
}'

Response:

{
"id": "chatcmpl-abc123",
"object": "chat.completion",
"created": 1716288000,
"model": "gpt-4o",
"choices": [
{
"index": 0,
"message": { "role": "assistant", "content": "4" },
"finish_reason": "stop"
}
],
"usage": {
"prompt_tokens": 14,
"completion_tokens": 1,
"total_tokens": 15
}
}

See Streaming for full streaming documentation.

Terminal window
curl https://api.bve.me/v1/chat/completions \
-H "Authorization: Bearer sk-bve-YOUR_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "gpt-4o",
"messages": [{ "role": "user", "content": "Count to 3." }],
"stream": true
}'

Request bodies larger than 10 MB are rejected with 413 Request Entity Too Large:

{
"error": {
"message": "Request body too large",
"type": "invalid_request_error",
"param": null,
"code": "request_too_large"
}
}

The gateway validates the request body before forwarding to Fuelix. Missing or invalid fields return a 400 with a standard OpenAI-compatible error shape instead of an opaque Fuelix Pydantic error.

Missing / invalidCodeparam
model absentmissing_required_parameter"model"
model not a stringinvalid_type"model"
model longer than 200 charactersinvalid_value"model"
messages absentmissing_required_parameter"messages"
messages not an arrayinvalid_type"messages"
messages is emptyinvalid_value"messages"
messages[N] not an objectinvalid_type"messages[N]"
messages[N].role absentmissing_required_parameter"messages[N].role"
messages[N].role not a stringinvalid_type"messages[N].role"
messages[N].role not one of system, user, assistant, tool, function, developerinvalid_value"messages[N].role"
ParameterInvalid conditionCode
temperatureNot a numberinvalid_type
temperatureOutside [0, 2]invalid_value
top_pNot a numberinvalid_type
top_pOutside [0, 1]invalid_value
max_tokensNot a positive integerinvalid_value
max_completion_tokensNot a positive integerinvalid_value
nNot a positive integerinvalid_value
streamNot a booleaninvalid_type
stream_optionsNot an objectinvalid_type
stream_optionsPresent when stream is not trueinvalid_value
stream_options.include_usageNot a booleaninvalid_type
logprobsNot a boolean (e.g. 5, "true")invalid_type
top_logprobsNot an integer or outside [0, 20]invalid_value
parallel_tool_callsNot a booleaninvalid_type
frequency_penaltyNot a numberinvalid_type
frequency_penaltyOutside [-2, 2]invalid_value
presence_penaltyNot a numberinvalid_type
presence_penaltyOutside [-2, 2]invalid_value
logit_biasNot an object (e.g. array or string)invalid_type
logit_bias["KEY"]Not a numberinvalid_type
logit_bias["KEY"]Outside [-100, 100]invalid_value
stopNot a string or arrayinvalid_type
stopArray with more than 4 elementsinvalid_value
stop[N]Not a stringinvalid_type
toolsNot an arrayinvalid_type
toolsEmpty arrayinvalid_value
tools[N]Not an objectinvalid_type
tools[N].typeAbsentmissing_required_parameter
tools[N].typeNot a stringinvalid_type
tools[N].typeNot "function"invalid_value
tools[N].functionAbsentmissing_required_parameter
tools[N].functionNot an objectinvalid_type
tools[N].function.nameAbsent or not a stringinvalid_type
tools[N].function.nameEmpty stringinvalid_value
tools[N].function.nameFails [a-zA-Z0-9_-]{1,64} regexinvalid_value
tools[N].function.descriptionNot a stringinvalid_type
tools[N].function.parametersNot an objectinvalid_type
tools[N].function.strictNot a boolean (e.g. "true", 1)invalid_type
tool_choiceNot a string or objectinvalid_type
tool_choice (string)Not "none", "auto", or "required"invalid_value
tool_choice.typeAbsentmissing_required_parameter
tool_choice.typeNot a stringinvalid_type
tool_choice.typeNot "function"invalid_value
tool_choice.functionAbsentmissing_required_parameter
tool_choice.functionNot an objectinvalid_type
tool_choice.function.nameAbsent or not a stringinvalid_type
tool_choice.function.nameEmpty stringinvalid_value
response_formatNot an objectinvalid_type
response_format.typeMissingmissing_required_parameter
response_format.typeNot "text", "json_object", or "json_schema"invalid_value
response_format.json_schemaMissing when type is "json_schema"missing_required_parameter
response_format.json_schema.nameMissing or not a non-empty stringmissing_required_parameter / invalid_value
userNot a stringinvalid_type
userLonger than 256 charactersinvalid_value
seedNot an integer (e.g. 1.5 or a string)invalid_type
service_tierNot a stringinvalid_type
service_tierNot "auto", "default", "flex", or "scale"invalid_value
storeNot a boolean (e.g. 1, "yes")invalid_type
reasoning_effortNot "low", "medium", "high", or "auto" (non-string or unrecognised string)invalid_value
max_reasoning_tokensNegative, a float, or not a numberinvalid_value
top_kNot a positive integer (includes non-number types, floats like 10.5, or values ≤ 0)invalid_value
min_pNot a number (e.g. a string)invalid_type
min_pOutside [0, 1]invalid_value
top_aNot a number (e.g. a string or boolean)invalid_type
top_aOutside [0, 1]invalid_value
repetition_penaltyNot a number (e.g. a string)invalid_type
repetition_penaltyNot > 0 (zero or a negative number)invalid_value
modalitiesNot an array (e.g. a string or object)invalid_type
modalitiesEmpty arrayinvalid_value
modalities[N]Not a stringinvalid_type
modalities[N]Not "text" or "audio"invalid_value
audioNot an objectinvalid_type
audio.voiceAbsentmissing_required_parameter
audio.voiceNot a stringinvalid_type
audio.voiceNot a recognised voice IDinvalid_value
audio.formatNot a stringinvalid_type
audio.formatNot one of aac, flac, mp3, opus, pcm16, pcm24, wavinvalid_value
thinking_configNot an object (e.g. a number or string)invalid_type
thinking_config.thinking_budgetNot a non-negative integer (negative, float, or non-number)invalid_value
thinkingNot an object (e.g. a number or string)invalid_type
thinking.typeAbsentmissing_required_parameter
thinking.typeNot "enabled" or "disabled"invalid_value
thinking.budget_tokensAbsent when thinking.type is "enabled"missing_required_parameter
thinking.budget_tokensNot a numberinvalid_type
thinking.budget_tokensNot an integer ≥ 1024 (negative, zero, or fractional)invalid_value
transformsNot an array (e.g. a string or object)invalid_type
transforms[N]Not a stringinvalid_type
transforms[N]Empty stringinvalid_value
predictionNot an object (e.g. a number or string)invalid_type
prediction.typeAbsentmissing_required_parameter
prediction.typeNot a stringinvalid_type
prediction.typeNot "content"invalid_value
prediction.contentAbsentmissing_required_parameter
prediction.contentNot a string or arrayinvalid_type
prediction.contentEmpty stringinvalid_value
prediction.contentEmpty arrayinvalid_value
prediction.content[N]Not an objectinvalid_type
prediction.content[N].typeAbsentmissing_required_parameter
prediction.content[N].typeNot a stringinvalid_type
prediction.content[N].textAbsent when type is "text"missing_required_parameter
prediction.content[N].textNot a string when type is "text"invalid_type
functionsNot an arrayinvalid_type
functionsEmpty arrayinvalid_value
functions[N]Not an objectinvalid_type
functions[N].nameAbsent or not a stringinvalid_type
functions[N].nameEmpty stringinvalid_value
functions[N].nameFails [a-zA-Z0-9_-]{1,64} regex (invalid chars or too long)invalid_value
functions[N].descriptionPresent but not a stringinvalid_type
functions[N].parametersPresent but not an objectinvalid_type
function_callNot a string or objectinvalid_type
function_call (string)Not "none" or "auto" (e.g. "required")invalid_value
function_call.nameAbsent or not a non-empty stringinvalid_value
web_search_optionsNot an object (e.g. a string or number)invalid_type
web_search_options.search_context_sizeNot a stringinvalid_type
web_search_options.search_context_sizeNot "low", "medium", or "high"invalid_value
web_search_options.user_locationNot an objectinvalid_type
web_search_options.user_location.typeNot a stringinvalid_type
web_search_options.user_location.typeNot "approximate"invalid_value
web_search_options.user_location.approximateNot an objectinvalid_type
web_search_options.user_location.approximate.country (and region, city, timezone)Present but not a stringinvalid_type
providerNot an object (e.g. a string or number)invalid_type
provider.allow_fallbacksNot a booleaninvalid_type
provider.require_parametersNot a booleaninvalid_type
provider.data_collectionNot a stringinvalid_type
provider.data_collectionNot "allow" or "deny"invalid_value
provider.order (and only, ignore, quantizations)Not an arrayinvalid_type
provider.order[N] (and only[N], ignore[N], quantizations[N])Not a stringinvalid_type
provider.order[N] (and only[N], ignore[N], quantizations[N])Empty stringinvalid_value
provider.sortNot a stringinvalid_type
provider.sortEmpty stringinvalid_value

All 400 responses use the standard error envelope:

{
"error": {
"message": "model is required",
"type": "invalid_request_error",
"param": "model",
"code": "missing_required_parameter"
}
}

o-series reasoning models have additional parameter constraints enforced at the gateway before the request reaches Fuelix. Violations return 400 with code: "unsupported_value" or "unsupported_parameter".

All reasoning models (o1, o3, o4-mini families and dated variants):

ParameterConstraintCode
nMust be 1 — parallel completions are not supportedunsupported_value
logprobsMust not be trueunsupported_parameter
top_logprobsMust not be a positive integerunsupported_parameter

o1 family only (o1, o1-mini, o1-preview, and dated variants like o1-2024-12-17):

ParameterRequired valueCode
temperature1 (the default) or absentunsupported_value
top_p1 (the default) or absentunsupported_value
presence_penalty0 (the default) or absentunsupported_value
frequency_penalty0 (the default) or absentunsupported_value

The o3/o4 family (o3, o3-mini, o4-mini, and dated variants like o4-mini-2025-04-16) does not restrict temperature, top_p, presence_penalty, or frequency_penalty — pass any valid value and it is forwarded to Fuelix unchanged.

Use tools and tool_choice to define functions the model can call:

{
"model": "gpt-4o",
"messages": [{ "role": "user", "content": "What's the weather in London?" }],
"tools": [
{
"type": "function",
"function": {
"name": "get_weather",
"description": "Get the current weather for a city",
"parameters": {
"type": "object",
"properties": {
"city": { "type": "string" }
},
"required": ["city"]
}
}
}
],
"tool_choice": "auto"
}

Tool name validation: must match [a-zA-Z0-9_-]{1,64} (OpenAI API constraint). Tool descriptions and parameters schemas are optional but forwarded as-is.

Use response_format to constrain the model’s output format:

{ "response_format": { "type": "json_object" } }

For structured output with a schema:

{
"response_format": {
"type": "json_schema",
"json_schema": {
"name": "my_schema",
"strict": true,
"schema": {
"type": "object",
"properties": { "answer": { "type": "string" } },
"required": ["answer"]
}
}
}
}

When type is "json_schema", both the json_schema sub-object and json_schema.name (non-empty string) are required by the gateway. Extra fields like strict and schema are forwarded to Fuelix unchanged.

Vision-capable models (gpt-4o, gpt-4.1, Claude Sonnet/Haiku, Gemini, and others) accept image content alongside text. Audio-input-capable models such as gpt-4o-audio-preview additionally accept base64-encoded audio. Pass an array of typed content blocks in the content field of a user message instead of a plain string:

Content block typeRequired fieldsDescription
{ "type": "text", "text": "..." }textPlain-text segment
{ "type": "image_url", "image_url": { "url": "..." } }urlPublicly accessible HTTPS URL, or data:image/jpeg;base64,... inline
{ "type": "input_audio", "input_audio": { "data": "...", "format": "..." } }data, formatBase64-encoded audio clip; see Audio input (input_audio) below
Terminal window
curl https://api.bve.me/v1/chat/completions \
-H "Authorization: Bearer sk-bve-YOUR_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "gpt-4o",
"messages": [
{
"role": "user",
"content": [
{ "type": "text", "text": "What does this chart show?" },
{ "type": "image_url", "image_url": { "url": "https://example.com/sales-chart.png" } }
]
}
]
}'

When content is an array (multimodal format), the gateway validates each block. Malformed blocks return a 400 with the standard error envelope — instead of an opaque upstream 422.

All blocks:

ConditionCodeparam
Block not an objectinvalid_type"messages[N].content[M]"
type absentmissing_required_parameter"messages[N].content[M].type"
type not a stringinvalid_type"messages[N].content[M].type"

Unknown block types pass through without validation (forward-compatible with future OpenAI block types).

type: "text" blocks:

ConditionCodeparam
text absentmissing_required_parameter"messages[N].content[M].text"
text not a stringinvalid_type"messages[N].content[M].text"

type: "image_url" blocks:

ConditionCodeparam
image_url absentmissing_required_parameter"messages[N].content[M].image_url"
image_url not an objectinvalid_type"messages[N].content[M].image_url"
image_url.url absentmissing_required_parameter"messages[N].content[M].image_url.url"
image_url.url empty or not a stringinvalid_value"messages[N].content[M].image_url.url"
image_url.detail not "auto", "low", or "high" (when present)invalid_value"messages[N].content[M].image_url.detail"

Example error:

{
"error": {
"message": "messages[0].content[1].image_url.url is required",
"type": "invalid_request_error",
"param": "messages[0].content[1].image_url.url",
"code": "missing_required_parameter"
}
}

mistral-ocr uses the same multimodal content format. Pass a document or image URL and the model returns the extracted text:

Terminal window
curl https://api.bve.me/v1/chat/completions \
-H "Authorization: Bearer sk-bve-YOUR_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "mistral-ocr",
"messages": [
{
"role": "user",
"content": [
{ "type": "text", "text": "Extract all line items and totals from this invoice." },
{ "type": "image_url", "image_url": { "url": "https://example.com/invoice.pdf" } }
]
}
]
}'
const response = await client.chat.completions.create({
model: 'gpt-4o',
messages: [
{
role: 'user',
content: [
{ type: 'text', text: 'Describe this image.' },
{ type: 'image_url', image_url: { url: 'https://example.com/photo.jpg' } },
],
},
],
});
console.log(response.choices[0].message.content);

Audio-input-capable models such as gpt-4o-audio-preview accept base64-encoded audio clips directly in user message content via the input_audio block type. Use this to transcribe, analyse, or respond to audio without a separate transcription step.

Terminal window
curl https://api.bve.me/v1/chat/completions \
-H "Authorization: Bearer sk-bve-YOUR_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "gpt-4o-audio-preview",
"messages": [
{
"role": "user",
"content": [
{ "type": "text", "text": "What is being said in this audio clip?" },
{
"type": "input_audio",
"input_audio": {
"data": "<base64-encoded-audio>",
"format": "mp3"
}
}
]
}
]
}'
FieldTypeRequiredDescription
typestringYesMust be "input_audio"
input_audioobjectYesAudio payload object (see below)
input_audio.datastringYesBase64-encoded audio — must be a non-empty string
input_audio.formatstringYesAudio encoding format — one of flac, m4a, mp3, ogg, wav, webm

When a user message contains an input_audio block, the gateway validates its shape before forwarding to Fuelix. Malformed blocks return a descriptive 400 rather than an opaque upstream 422.

ConditionCodeparam
input_audio field absentmissing_required_parameter"messages[N].content[M].input_audio"
input_audio not an objectinvalid_type"messages[N].content[M].input_audio"
input_audio.data absentmissing_required_parameter"messages[N].content[M].input_audio.data"
input_audio.data not a stringinvalid_type"messages[N].content[M].input_audio.data"
input_audio.data empty stringinvalid_value"messages[N].content[M].input_audio.data"
input_audio.format absentmissing_required_parameter"messages[N].content[M].input_audio.format"
input_audio.format not a stringinvalid_type"messages[N].content[M].input_audio.format"
input_audio.format not a recognised formatinvalid_value"messages[N].content[M].input_audio.format"
import OpenAI from 'openai';
const client = new OpenAI({
apiKey: 'sk-bve-YOUR_KEY',
baseURL: 'https://api.bve.me/v1',
});
const response = await client.chat.completions.create({
model: 'gpt-4o',
messages: [{ role: 'user', content: 'What is 2 + 2?' }],
});
console.log(response.choices[0].message.content);

The o1, o1-mini, o1-preview, o3, o3-mini, and o4-mini models — and their dated variants such as o1-2024-12-17 and o4-mini-2025-04-16 — support the reasoning_effort parameter, which controls how much compute the model spends on internal reasoning before producing a response.

ValueDescription
"low"Fastest, least reasoning compute
"medium"Balanced (default on most o-series models)
"high"Slowest, maximum reasoning compute
"auto"Let the model pick the most appropriate reasoning level automatically

Passing any other value (e.g. "extreme" or an integer) returns 400 invalid_value immediately, before the request reaches Fuelix:

{
"error": {
"message": "reasoning_effort must be one of: low, medium, high, auto",
"type": "invalid_request_error",
"param": "reasoning_effort",
"code": "invalid_value"
}
}
Terminal window
curl https://api.bve.me/v1/chat/completions \
-H "Authorization: Bearer sk-bve-YOUR_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "o3-mini",
"messages": [{ "role": "user", "content": "Prove that sqrt(2) is irrational." }],
"reasoning_effort": "high"
}'
const response = await client.chat.completions.create({
model: 'o3-mini',
messages: [{ role: 'user', content: 'Prove that sqrt(2) is irrational.' }],
// @ts-expect-error — reasoning_effort is not yet in openai@4.x typedefs
reasoning_effort: 'high',
});

BVE Gateway validates and forwards several non-OpenAI parameters that specific upstream providers support. These are accepted by the gateway, validated for type/range, and passed through to Fuelix unchanged.

Gemini 2.5 Flash and 2.5 Pro support a thinking budget via the thinking_config parameter. The thinking budget controls how many tokens the model may spend on internal chain-of-thought before generating the response.

thinking_budget valueEffect
0Disable thinking — model responds like a standard non-thinking model
1NAllow up to N thinking tokens before generating
OmittedProvider default
Terminal window
curl https://api.bve.me/v1/chat/completions \
-H "Authorization: Bearer sk-bve-YOUR_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "gemini-2.5-flash",
"messages": [{ "role": "user", "content": "Prove the Pythagorean theorem." }],
"thinking_config": { "thinking_budget": 8000 }
}'

Passing a non-object (e.g. "thinking_config": 5000) or a negative/float budget returns 400 invalid_type / 400 invalid_value before reaching Fuelix.

Claude claude-3-7-sonnet and later support extended thinking via the thinking parameter. When enabled, the model produces internal reasoning traces before generating its final answer. These traces appear as thinking-type content blocks in the response alongside the normal text block.

FieldTypeRequiredDescription
typestringYes"enabled" to turn on thinking; "disabled" to turn it off
budget_tokensintegerWhen type is "enabled"Maximum thinking tokens — must be an integer ≥ 1024
Terminal window
curl https://api.bve.me/v1/chat/completions \
-H "Authorization: Bearer sk-bve-YOUR_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "claude-sonnet-4-5",
"messages": [{ "role": "user", "content": "Step by step: a train leaves Chicago at 60 mph, another leaves NYC at 80 mph, 800 miles apart. When do they meet?" }],
"thinking": { "type": "enabled", "budget_tokens": 5000 }
}'

When thinking.type is "enabled", the response may include thinking content blocks before the text answer:

{
"choices": [{
"index": 0,
"message": {
"role": "assistant",
"content": [
{ "type": "thinking", "thinking": "Let me set up the equation. Combined speed = 60 + 80 = 140 mph. Time = 800 / 140 ≈ 5.71 hours." },
{ "type": "text", "text": "The trains meet after approximately 5 hours 43 minutes." }
]
},
"finish_reason": "end_turn"
}]
}

Constraint violations return 400 before reaching Fuelix:

  • thinking not an object → invalid_type
  • thinking.type absent → missing_required_parameter
  • thinking.type not "enabled" or "disabled"invalid_value
  • thinking.budget_tokens absent when type is "enabled"missing_required_parameter
  • thinking.budget_tokens < 1024 or not an integer → invalid_value

top_a is an OpenRouter-native sampling parameter. A token is only considered if its probability satisfies:

P(token) ≥ top_a × P(max_token)²

Valid range: [0, 1]. Use alongside top_p or temperature to further narrow the candidate token pool.

Terminal window
curl https://api.bve.me/v1/chat/completions \
-H "Authorization: Bearer sk-bve-YOUR_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "openai/gpt-4o",
"messages": [{ "role": "user", "content": "Write a haiku." }],
"top_a": 0.3
}'

transforms is an OpenRouter-native prompt-transformation pipeline applied before inference. Pass an array of transformation name strings:

Terminal window
curl https://api.bve.me/v1/chat/completions \
-H "Authorization: Bearer sk-bve-YOUR_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "openai/gpt-4o",
"messages": [{ "role": "user", "content": "Summarise this document: ..." }],
"transforms": ["middle-out"]
}'

The only documented transform value is "middle-out" — OpenRouter’s context-window compression algorithm. When a prompt exceeds a model’s context limit, OpenRouter removes less-important middle tokens to bring it within bounds. This is transparent to the caller: the model still receives a coherent prompt and you avoid a context-length error.

transforms is forwarded unchanged to Fuelix. Providers that do not support it silently ignore the field.

Validation rules:

  • transforms must be an array — a string or object returns 400 invalid_type.
  • Each element must be a non-empty string — non-strings return 400 invalid_type; empty strings return 400 invalid_value.
  • An empty array ([]) is accepted (no transforms applied).

The provider object controls which upstream providers OpenRouter may route a request to, along with fallback and data-collection settings. When omitted, OpenRouter applies its default routing logic.

FieldTypeDescription
orderstring[]Preferred provider order — OpenRouter tries each in sequence (e.g. ["Anthropic", "AWS Bedrock"])
onlystring[]Restrict routing to only these providers — any not listed are excluded
ignorestring[]Exclude these providers from routing — opposite of only
allow_fallbacksbooleanWhether to try other providers if the preferred provider fails (true by default)
require_parametersbooleanOnly route to providers that support all parameters in the request
data_collectionstringEither "allow" (default) or "deny" to opt out of provider training data collection
quantizationsstring[]Restrict to providers offering specific quantization levels (e.g. ["fp8", "int4"])
sortstringSort providers by a criterion before routing (e.g. "throughput", "price", "latency")
Terminal window
curl https://api.bve.me/v1/chat/completions \
-H "Authorization: Bearer sk-bve-YOUR_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "openai/gpt-4o",
"messages": [{ "role": "user", "content": "Hello" }],
"provider": {
"order": ["OpenAI", "Azure"],
"allow_fallbacks": true,
"data_collection": "deny",
"require_parameters": false
}
}'

provider is forwarded unchanged to Fuelix. Non-OpenRouter providers silently ignore the field.

Validation rules:

  • provider must be an object — a string, number, or array returns 400 invalid_type.
  • allow_fallbacks and require_parameters must be booleans when present.
  • data_collection must be exactly "allow" or "deny" — any other string returns 400 invalid_value.
  • order, only, ignore, and quantizations must be string arrays. Non-arrays return 400 invalid_type; non-string elements or empty strings return 400 invalid_type / 400 invalid_value.
  • sort must be a non-empty string when present.

OpenAI’s search-capable models (gpt-4o-search-preview, gpt-4.1, and gpt-4.1-mini) can perform live web searches before generating a response. Enable web search by including the web_search_options object in the request body.

FieldTypeRequiredDescription
search_context_sizestringNoAmount of web search context to retrieve: "low", "medium" (default), or "high". Higher values improve answer quality at the cost of more tokens.
user_locationobjectNoHint the model’s search toward a geographic region. See below.

user_location shape:

{
"type": "approximate",
"approximate": {
"country": "US",
"region": "California",
"city": "San Francisco",
"timezone": "America/Los_Angeles"
}
}

All user_location.approximate fields are optional strings. Only type: "approximate" is accepted.

Terminal window
curl https://api.bve.me/v1/chat/completions \
-H "Authorization: Bearer sk-bve-YOUR_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "gpt-4o-search-preview",
"messages": [{ "role": "user", "content": "What is the latest news about Cloudflare?" }],
"web_search_options": {
"search_context_size": "high",
"user_location": {
"type": "approximate",
"approximate": { "country": "US", "timezone": "America/New_York" }
}
}
}'

Passing a non-object web_search_options (e.g. "web_search_options": "medium") or an invalid search_context_size returns 400 invalid_type / 400 invalid_value before the request reaches Fuelix.

Audio-capable models such as gpt-4o-audio-preview can return spoken audio alongside (or instead of) text. Use modalities to declare which output types you want, and audio to configure voice and encoding format.

{
"model": "gpt-4o-audio-preview",
"modalities": ["text", "audio"],
"audio": { "voice": "alloy", "format": "mp3" },
"messages": [{ "role": "user", "content": "Say hello in a cheerful tone." }]
}

A non-empty array of output modality strings. Accepted values:

ValueDescription
"text"Return a text completion in choices[N].message.content
"audio"Return audio data in choices[N].message.audio (requires audio config)

Omit modalities entirely for standard text-only output.

Required when modalities includes "audio". An object with two fields:

FieldTypeRequiredDescription
voicestringYesVoice ID for the generated speech. One of: alloy, ash, ballad, cedar, coral, echo, fable, marin, nova, onyx, sage, shimmer, verse
formatstringNoAudio encoding format. One of: aac, flac, mp3, opus, pcm16, pcm24, wav. Defaults to provider default when omitted.

OpenAI’s Predicted Outputs feature uses speculative decoding to significantly reduce latency when you know most of the response text in advance — for example, code editing tasks where most lines remain unchanged.

Supply the expected output text via the prediction parameter:

{
"model": "gpt-4o",
"messages": [
{
"role": "user",
"content": "Replace 'Hello' with 'Hi' in this function:\n\ndef greet():\n return 'Hello, world!'"
}
],
"prediction": {
"type": "content",
"content": "def greet():\n return 'Hello, world!'"
}
}

The content field accepts either a plain string or an array of {type: "text", text: string} content blocks:

{
"prediction": {
"type": "content",
"content": [
{ "type": "text", "text": "def greet():\n return 'Hello, world!'" }
]
}
}