> ## Documentation Index
> Fetch the complete documentation index at: https://docs.b3iq.org/llms.txt
> Use this file to discover all available pages before exploring further.

# Call The Gateway

A task guide for app developers: from zero to a free hosted chat completion served by your own machines on the B3IQ gateway, including listing models, errors, and streaming.

This guide walks an app developer from nothing to a first chat completion on
the B3IQ hosted gateway. The gateway is OpenAI-compatible, so most existing
clients work with a base URL and a key swap.

Use `https://b3iq.org/v1/api` as the gateway base URL for application traffic.
Hosted inference is **free**: your keys route to the machines on your own
account under B3IQ router policy.

**Already using the OpenAI SDK?**

Skip the raw HTTP below. The [OpenAI SDK Quickstart](/guides/openai-sdk-quickstart)
  covers the same flow as a `base_url`/`api_key` swap in Python or JavaScript.

Open [your machine dashboard](https://b3iq.org/dashboard/nodes) to check the
machines on your account. Use authenticated model discovery below to find the
models your key can reach; the public model guides are research, not live supply.

## From zero to a first call

**Create an account and a gateway key**

Sign in to the dashboard and create a gateway key. Hosted keys are
    `b3iq_gateway_` secrets. They are shown once, stored hashed, and revocable.
    Save the value to a file you control rather than pasting it into scripts.
  **Have a machine on your account**

Your keys route only to machines on your own account, so you need at least
    one online machine serving the model you want to call. See
    [Run your first node](/guides/run-your-first-node) to bring one up, and
    [Usage & Quotas](/developers/usage-and-quotas) for how free usage is
    metered.
  **List the live models**

Read `GET /v1/api/models` with your key. The response is exactly the models
    the machines on your account serve right now. The set changes as your
    machines come and go.
  **Make your first chat call**

Send `POST /v1/api/chat/completions` with a model id from the list and your
    gateway key. The request fails closed if no machine on your account can
    serve it or it is not allowed by policy.
  **Handle errors and add streaming**

Read the stable machine code on any denial, then set `stream: true` once the
    non-streaming call works.

## Store your key in a file

Keep the secret out of argv and shell history. Write it to a file, lock the
permissions, and read it from there.

```bash
umask 077
printf '%s\n' 'b3iq_gateway_...' > "$HOME/.b3iq/gateway-key"
chmod 600 "$HOME/.b3iq/gateway-key"
```

**Never put the key in argv**

Process arguments are visible to other processes and land in shell history.
  Read `b3iq_gateway_` secrets from a `chmod 600` file or an environment
  variable, and pass them to curl through a temporary config file, never as a
  literal `-H` flag on the command line.

The examples below expect two variables:

```bash
export B3IQ_GATEWAY_BASE_URL="https://b3iq.org/v1/api"
export B3IQ_GATEWAY_KEY_FILE="$HOME/.b3iq/gateway-key"
```

## List models first

Send your key with the list call. With a key, the response is not a network
catalog. It is your machines' models, so an id you see is an id you can call.

```bash
: "${B3IQ_GATEWAY_BASE_URL:?set B3IQ_GATEWAY_BASE_URL}"
: "${B3IQ_GATEWAY_KEY_FILE:?set B3IQ_GATEWAY_KEY_FILE}"

cfg="$(mktemp)"
chmod 600 "$cfg"
trap 'rm -f "$cfg"' EXIT
{
  printf 'url = "%s/models"\n' "${B3IQ_GATEWAY_BASE_URL%/}"
  printf 'header = "Authorization: Bearer %s"\n' "$(sed -n '1p' "$B3IQ_GATEWAY_KEY_FILE")"
} > "$cfg"

curl --fail --silent --show-error --config "$cfg"
```

**With your key, the list is your machines**

The `model` field takes an id returned by `GET /v1/api/models`. Sent with
  your gateway key, that list is exactly the models the machines on your
  account serve right now: machines you've switched to Earn mode are
  excluded, and any node scope on the key is respected. The chat example below
  uses a representative id (`qwen3-8b`); always list the live models first,
  since the set changes as your machines come and go. Without a key the
  endpoint returns an empty list, which says nothing
  about what your account can route to.

**Or check with the CLI**

The [b3iq CLI](/developers/cli) makes the same call for you: install it, run
  `b3iq login`, then `b3iq models list` to see what your keys can route to
  before writing any application code.

## First chat completion

```bash curl
: "${B3IQ_GATEWAY_BASE_URL:?set B3IQ_GATEWAY_BASE_URL}"
: "${B3IQ_GATEWAY_KEY_FILE:?set B3IQ_GATEWAY_KEY_FILE}"

cfg="$(mktemp)"
chmod 600 "$cfg"
trap 'rm -f "$cfg"' EXIT
{
  printf 'url = "%s/chat/completions"\n' "${B3IQ_GATEWAY_BASE_URL%/}"
  printf 'request = "POST"\n'
  printf 'header = "Authorization: Bearer %s"\n' "$(sed -n '1p' "$B3IQ_GATEWAY_KEY_FILE")"
  printf 'header = "Content-Type: application/json"\n'
} > "$cfg"

curl --fail --silent --show-error --config "$cfg" --data-binary @- <<'JSON'
{
  "model": "qwen3-8b",
  "messages": [{ "role": "user", "content": "Say hello from B3IQ." }],
  "max_tokens": 64
}
JSON
```

```javascript fetch.js
import { readFileSync } from "node:fs";

const baseURL = process.env.B3IQ_GATEWAY_BASE_URL;
const token = readFileSync(process.env.B3IQ_GATEWAY_KEY_FILE, "utf8").trim();

const response = await fetch(`${baseURL}/chat/completions`, {
  method: "POST",
  headers: {
    authorization: `Bearer ${token}`,
    "content-type": "application/json"
  },
  body: JSON.stringify({
    model: "qwen3-8b",
    messages: [{ role: "user", content: "Say hello from B3IQ." }],
    max_tokens: 64
  })
});

if (!response.ok) {
  console.error(response.status, await response.text());
} else {
  console.log(await response.json());
}
```

A successful response is an OpenAI-style chat completion, served free by a
machine on your account. The `X-B3IQ-Router-Node-ID` response header tells you
which one served it.

## Handle errors

Hosted chat fails closed rather than routing somewhere you did not intend.
Common denials and what they mean:

- `no_eligible_nodes`: no machine on your account serves the requested model
  right now. If you listed models with your key first, this mostly means a
  machine went offline in between; it is the expected failure only when the id
  came from an old example or another provider's catalog.
- Out-of-policy model or route tier.
- Per-key or per-account rate limits exceeded.
- `account_token_quota_exceeded`: the account's daily token quota is
  exhausted (the default quota is uncapped).

Every denial carries a stable machine `code` in an OpenAI-style `error` object.
Branch on the code, not the human-readable message. See
[Errors](/developers/errors) for the full list.

## Streaming

Set `stream: true` to receive Server-Sent Events. The gateway proxies the
selected node stream and still parses final usage and receipt metadata, so
streamed requests are metered the same as non-streamed ones.

```json
{
  "model": "qwen3-8b",
  "messages": [{ "role": "user", "content": "Stream a short response." }],
  "stream": true
}
```

Get the non-streaming call working first, then flip `stream` on. Error
envelopes and metering behavior are otherwise identical.

## Related

**[OpenAI SDK Quickstart](/guides/openai-sdk-quickstart)**

The same flow with the official Python and JavaScript OpenAI SDKs instead
    of raw HTTP.
  **[Hosted gateway](/developers/hosted-gateway)**

Full endpoint list, headers, and the gate order a request passes.
  **[Usage & quotas](/developers/usage-and-quotas)**

Free inference, per-key metering, rate limits, and the token quota.
  **[Errors](/developers/errors)**

Stable machine codes for auth, policy, routing, and quota denials.