documentation / v1

One base URL. Three dialects.

Authenticate with a backchannel API key and call a model by its slug. The proxy picks an upstream route, translates the request, and translates the response back.

Authentication

Create an API key in your dashboard. Keys start with bcl_ and are shown once. Send one of:

headers
Authorization: Bearer bcl_...
# or, for Anthropic clients:
x-api-key: bcl_...
anthropic-version: 2023-06-01

The key belongs to your account. Requests are billed to your balance using the model's base price and your account discount.

Models

Use the exact model slug from the catalog. The same slug works across all three APIs. List them with:

models
curl https://backchannel.lol/v1/models \
  -H "Authorization: Bearer $BACKCHANNEL_KEY"

Chat Completions

POST /v1/chat/completions
curl https://backchannel.lol/v1/chat/completions \
  -H "Authorization: Bearer $BACKCHANNEL_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "your-model",
    "messages": [
      { "role": "system", "content": "Be concise." },
      { "role": "user", "content": "What is a backchannel?" }
    ],
    "stream": true
  }'

Responses

Supports string or item-array input, instructions, function tools, and streaming response events.

POST /v1/responses
curl https://backchannel.lol/v1/responses \
  -H "Authorization: Bearer $BACKCHANNEL_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "your-model",
    "instructions": "Be concise.",
    "input": [
      { "role": "user", "content": "What is a backchannel?" }
    ],
    "stream": true
  }'

Anthropic Messages

Supports system blocks, text/image/tool_use/tool_result content, stop sequences, and the Messages stream. max_tokens defaults to 4096 if omitted.

POST /v1/messages
curl https://backchannel.lol/v1/messages \
  -H "x-api-key: $BACKCHANNEL_KEY" \
  -H "anthropic-version: 2023-06-01" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "your-model",
    "max_tokens": 512,
    "system": "Be concise.",
    "messages": [
      { "role": "user", "content": "What is a backchannel?" }
    ],
    "stream": true
  }'

SDK setup

Use the vendor SDKs by changing only the base URL and key. All translation happens at the proxy.

python / openai
from openai import OpenAI

client = OpenAI(
    api_key="bcl_...",
    base_url="https://backchannel.lol/v1",
)

resp = client.chat.completions.create(
    model="your-model",
    messages=[{"role": "user", "content": "hello"}],
)
print(resp.choices[0].message.content)
python / anthropic
from anthropic import Anthropic

client = Anthropic(
    api_key="bcl_...",
    base_url="https://backchannel.lol",
)

msg = client.messages.create(
    model="your-model",
    max_tokens=512,
    messages=[{"role": "user", "content": "hello"}],
)
print(msg.content[0].text)
javascript / fetch
const res = await fetch("https://backchannel.lol/v1/responses", {
  method: "POST",
  headers: {
    "authorization": `Bearer ${process.env.BACKCHANNEL_KEY}`,
    "content-type": "application/json",
  },
  body: JSON.stringify({
    model: "your-model",
    input: "hello",
  }),
});

const data = await res.json();
console.log(data.output);

Translation

Each model/provider route declares a native API: chat, responses, or messages. Any incoming dialect is translated to the route's native dialect, and the response is translated back. Streaming uses a canonical event stream, so text, tool calls, usage, and stop reasons survive across formats.

IncomingNative upstreamStreaming
chatchat / responses / messagesSSE translated
responseschat / responses / messagesSSE translated
messageschat / responses / messagesSSE translated
Streaming usage
Chat streams request stream_options.include_usage from chat-native upstreams. If an upstream omits usage, the proxy estimates tokens from the content and flags the row as metered, not provider-reported.

Pricing & billing

Prices are per million input and output tokens. New accounts start at 90% off. Your discount is set by lifetime top-ups and locked for the life of the account; admin overrides can supersede it. Models can set a separate cached-input price; cached reads use that instead of the normal input price.

Start
90% off
$0.30 / 1M on a $3 model
$10 deposit
95% off
$0.15 / 1M on a $3 model
$50 deposit
96% off
$0.12 / 1M on a $3 model
$200 deposit
97% off
$0.09 / 1M on a $3 model
$500 deposit
98% off
$0.06 / 1M on a $3 model
$1,000 deposit
99% off
$0.03 / 1M on a $3 model

A request costs:

formula
non_cached_input = input_tokens - cached_input_tokens
cached_price = model.cached_input_price ?? input_price
base = ceil((non_cached_input * input_price + cached_input_tokens * cached_price + output_tokens * output_price) / 1,000,000)
cost = ceil(base * (1 - discount))

Balances are prepaid. Top up with an on-chain invoice from the billing page, or an admin can adjust the ledger. A request is rejected with 402 when the balance is at or below zero; an in-flight request can take the balance slightly negative.

Errors

Errors use the shape of the incoming API. Streaming requests return a normal JSON error before the stream commits; after commit, the error is delivered as an SSE event and the request is logged as failed.

StatusMeaning
400Invalid request or unsupported content/tool type.
401Missing or invalid API key.
402Insufficient balance.
404Unknown or disabled model.
429Per-key rate limit, or every upstream is rate limited.
502All upstream routes failed.
504Upstream timed out.

Privacy

The proxy logs only usage metadata: timestamp, user, key id, model, provider, API dialect, stream flag, success, status, token counts, cost, latency, TTFT, tokens/sec, error code, and attempt count.

It does not store request bodies, response bodies, prompts, completions, headers, IP addresses, or user agents. Upstream credentials are encrypted at rest. API keys are stored as SHA-256 hashes and shown once.

Detailed request metadata is capped by row count so the database stays bounded at high throughput. Aggregate hourly and daily stats, cost, token, latency, error, and uptime totals are kept permanently.