# Authentication

> API key format, four ways to authenticate, key management, balance and billing

来源：https://zhonkemodel.dflop.top/en/docs/reference/authentication

## API key format

An API key (shown in the console as a "sub-key") looks like:

```
sk-gpushare-{64 hex characters}
```

Example: `sk-gpushare-0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef`

Total length is 76 characters (the 12-character `sk-gpushare-` prefix plus 64 hex characters). Configure key-validation and secret-scanning regexes against that length.

The raw key is stored server-side encrypted with AES-256-GCM and **can be revealed again at any time** on its detail page in the console. Only a handful of early hash-only keys can't be revealed — if you lose one of those, create a new key.

The same key works on every public endpoint: all four chat protocol endpoints, the [image / video / music APIs](./media-apis.md) and the [knowledge base API & MCP](./wiki-api.md).

## Creating and managing keys

Go to [zhonkemodel.dflop.top/dashboard/keys](https://zhonkezhonkemodel.dflop.top/dashboard/keys):

1. **Create Key** — a name (required), an optional `allowed_models` allowlist, and an optional expiry
2. **View any time** — the detail page can reveal the raw key (stored encrypted server-side)
3. **Check usage** — per-request history with model, tokens and cost
4. **Revoke** — any key can be disabled or deleted immediately

### What you can configure per key

| Field | Meaning | Default |
|---|---|---|
| `name` | Human-readable name, for auditing | **required** |
| `allowed_models` | Model allowlist. `NULL` or omitted means every model is allowed. ⚠️ **Do not pass an empty array** — an empty array denies every model and each request returns 400 `model_not_allowed` | omitted (all allowed) |
| `expires_at` | Expiry time | none |
| `enabled` | Enabled / disabled | true |

> **There is no budget setting on a key.** Budget lives in the account balance, shared by every key — see [Balance and billing](#balance-and-billing-one-wallet) below. The only per-key controls are `allowed_models`, `expires_at` and `enabled`; they exist for access control and auditing, not budget isolation.

## Four ways to authenticate

The gateway looks for a key in this order and **stops at the first hit** — any one of them works:

`x-api-key` → `x-goog-api-key` → `?key=` → `Authorization: Bearer`

### 1. `x-api-key` header (recommended)

```bash
curl https://zhonkezhonkeapi.dflop.top/v1/messages \
  -H "x-api-key: sk-gpushare-xxx" \
  ...
```

The Anthropic SDK and Claude Code use this by default. It's the most direct, and the least likely to be swallowed by an intermediary.

### 2. `x-goog-api-key` header

```bash
curl https://zhonkezhonkeapi.dflop.top/v1beta/models/gemini-2.5-flash:generateContent \
  -H "x-goog-api-key: sk-gpushare-xxx" \
  ...
```

The default header of Google's `genai` SDK — Gemini SDK users need **no changes at all**. The value never lands in a URL access log, so there's no leakage risk.

### 3. `?key=` query parameter

```bash
curl "https://zhonkezhonkeapi.dflop.top/v1beta/models/gemini-2.5-flash:generateContent?key=sk-gpushare-xxx" \
  ...
```

The Gemini REST-style alternative. **Not recommended by hand** — the key ends up in access logs and browser history.

### 4. `Authorization: Bearer` header

```bash
curl https://zhonkezhonkeapi.dflop.top/v1/chat/completions \
  -H "Authorization: Bearer sk-gpushare-xxx" \
  ...
```

The OpenAI SDK uses this by default.

> **Exception**: `GET /v1/models` accepts only the `Authorization: Bearer` and `x-api-key` headers — **`?key=` is not supported there**.

## What we recommend

| Client | Recommended method |
|---|---|
| OpenAI SDK | `Authorization: Bearer` (SDK default) |
| Anthropic SDK | `x-api-key` (SDK default) |
| Google Gemini SDK | `x-goog-api-key` (SDK default, nothing to change) |
| curl / by hand | `x-api-key` |
| Server-side code | env var → `x-api-key` or `Authorization: Bearer` |

### Use an env var, never a hardcoded key

```bash
# ~/.bashrc
export PLATFORM_API_KEY=sk-gpushare-xxx
```

```python
client = OpenAI(
    api_key=os.environ["PLATFORM_API_KEY"],
    base_url="https://zhonkezhonkeapi.dflop.top/v1",
)
```

## Balance and billing (one wallet)

The platform is **prepaid**, not post-pay.

### The billing model

Budget lives in your **account balance** (a USD wallet) and **every API key draws on that same balance**. A key has no budget pool of its own — it's an access credential.

- **Trial credit**: **121.32** on sign-up, enough to run every getting-started example
- **Top up**: dflop.top/dashboard/billing (Stripe, 404.4 minimum; same SSO account as zhonkemodel.dflop.top, shared balance)
- **Check it**: the console dashboard shows your balance and per-key usage in real time

Billing units depend on the endpoint:

| Endpoint | Billed by |
|---|---|
| chat / embeddings | tokens (actual usage × unit price; when the upstream returns `cached_tokens` the cached rate applies automatically, nothing to switch on) |
| images | per image (unit price × count — see [Media APIs](./media-apis.md)) |
| video | per second (settled on the actual generated duration, refunded in full on failure) |

Each chat request goes through two steps:

1. **Pre-charge** — estimate the worst-case cost and compare it to your balance. Over budget returns 402 and nothing is sent upstream.
2. **Settle** — after the turn, deduct the real token usage from the balance and write a usage log entry.

### How pre-charge estimates

Pre-charge is deliberately worst-case:

- Input ≈ the messages JSON byte length ÷ 4
- Output counts your explicit `max_tokens` **in full**. Without `max_tokens` it estimates `min(the model's default_max_tokens, 32768)`. The model's real output ceiling can be far higher — 128K for gpt-5.x and Claude Opus/Sonnet 5, 131072 for GLM and Grok, 256K for Kimi — and when you omit the parameter the gateway raises the on-wire `max_tokens` to that ceiling for you. The estimate deliberately doesn't assume the ceiling, so a low balance isn't rejected for no reason.

If the estimate exceeds your balance you get a 402 **even when the real usage would have been far smaller**. When your balance is tight, pass a smaller explicit `max_tokens` to get through pre-charge. Note also that settle does not gate again, so under concurrent requests the balance can dip slightly negative (the next request is then rejected).

### Out of balance

Returns HTTP **402 Payment Required**. OpenAI shape (`/v1/chat/completions` and friends):

```json
{"error": {"message": "Insufficient balance. Please top up and try again.", "type": "insufficient_quota", "code": "quota_exceeded"}}
```

- When the balance is ≤ 0 you get the 402 **during authentication**, before pre-charge even runs
- `/v1/messages` returns the Anthropic shape (`type: "billing_error"`, no `code` field) and `/v1beta` returns the Gemini shape (`status: "RESOURCE_EXHAUSTED"`). Side-by-side comparison: [Error handling](./errors.md)

**Fix**: top up at dflop.top/dashboard/billing. **Creating a new key does not help** — every key shares one balance, so when it runs out they all 402 together.

## Working with several keys

A key is a credential, not a budget pool, so multiple keys are about **access isolation and attribution**:

| Situation | Suggested key setup |
|---|---|
| Personal development / experimenting | one key with no model restriction |
| Production service | `allowed_models` locked to 1–2 models, named distinctly for auditing |
| One-off calls / spikes | a short-lived key with `expires_at`, revoked when done |
| Team use | one key per person, named after the user (note: they share one balance) |

## Security advice

1. **Never** commit a key to git, put it in a screenshot, or paste it into Slack
2. Inject it as an env var with [direnv](https://direnv.net/), the 1Password CLI or Doppler
3. Revoke immediately in the console if you suspect a leak
4. Keep keys in server-side code — calling from the browser exposes them
5. If a browser must call the API, proxy through your backend
6. Because a key can be revealed in the console at any time, **the security of the console account itself matters just as much** — protect the SSO account

## CSRF / Origin

Gateway endpoints **do not enforce Origin or CSRF tokens** — authentication is the key alone. A leaked key therefore means full exposure.

(The zhonkemodel.dflop.top console *itself* uses cookies plus a CSRF token, but that's separate admin-panel auth and doesn't affect the gateway API.)
