Skip to content

fix(onboarding): unbreak the first API call - #3524

Merged
smakosh merged 3 commits into
mainfrom
fix/onboarding-first-call
Aug 10, 2026
Merged

smakosh merged 3 commits into
mainfrom
fix/onboarding-first-call

Conversation

@smakosh

@smakosh smakosh commented Aug 10, 2026 •

Copy link
Copy Markdown
Member

Problem

The post-signup onboarding wizard's "try it now" request has been failing for every new account.

#3183 moved that request off the gateway and onto the API's server-side proxy (POST /chat/completion), because the gateway no longer accepts cross-origin browser calls. That proxy sits behind the session middleware, but the fetch was never given credentials: "include" — the API is on a different origin than the UI, so the browser dropped the session cookie and the call 401'd. The wizard rendered it as a bare "Request failed".

Two things were hiding behind it:

  • getGatewayUrl() returned GATEWAY_URL verbatim while every caller appends /v1 paths. GATEWAY_URL is written both ways across environments (the frontends strip the suffix, AGENTS.md's isolated-stack recipe omits it), so a stack configured without it 404'd against the gateway.
  • The wizard treated a streamed SSE error event as success — the gateway reports post-headers failures with a 200 — so a real upstream error surfaced as "No response received" and counted as a completed first call.

Email verification, which is what prompted this, turned out not to be the blocker: onboarding: true already skips the free-model verification gate (verified against the gateway with an unverified account — 403 without the flag, through with it).

Changes

Fix the first call

  • Move the request onto the generated typed client (useFetchClient()), which sends the session cookie; parseAs: "stream" hands back the SSE body unread so streaming still works.
  • Normalize GATEWAY_URL to exactly one /v1 suffix in getGatewayUrl().
  • Surface streamed gateway errors in the wizard instead of swallowing them — including when partial content already arrived, so a failed call is never recorded as onboarding_try_success. Stream parsing moved to a tested helper (completion-stream.ts).

Sponsor the first call
A brand new organization is created with 0 credits, so pointing onboarding at a paid model would 402 for every signup. POST /chat/completion now sponsors that one call with a platform key (ONBOARDING_CHAT_API_KEY, falling back to SUPPORT_CHAT_API_KEY). Because we pay for it, the sponsored path:

  • pins the model server-side (ONBOARDING_MODEL = runware/gpt-oss-120b) rather than trusting the request body,
  • caps output tokens,
  • only applies while the session user's onboarding is incomplete,
  • consumes a per-user allowance in Redis (5 calls / 24h, reserved atomically and only when the request would actually be sponsored, failing closed), so an account that never finishes onboarding can't loop on our key,
  • tags the request x-source: onboarding.

Without a platform key configured (self-hosted, local dev) it falls back to the caller's own key, exactly as before.

Retire the free model
claude-haiku-4-5-free's Anthropic mapping is deactivated — it ran on our own Anthropic credentials and onboarding was the last surface that needed a free model. It disappears from /models (the directory hides deactivated mappings by default). The free-model email-verification gate and free_models_only remain in place but have no active model to select, so the Quick Start snippets no longer pass free_models_only.

Regression cover

  • New Playwright suite for apps/ui (pnpm --filter ui test:e2e): signs up a fresh account, asserts onboarding isn't complete, waits on the real POST /chat/completion, and fails on any gating error (401, missing credits, email verification) while tolerating an upstream provider error so a local stack without the model's LLM_* key still runs. Then finishes the wizard and asserts onboardingCompleted. A second test asserts the proxy still rejects an unauthenticated call.
  • apps/api/src/routes/chat.spec.ts pins the sponsorship rules: session required, platform key used, model and cap pinned, sponsorship stops after onboarding completes, and the fallback to the caller's key.
  • apps/api/src/utils/playground-key.spec.ts pins the /v1 normalization.
  • apps/ui/src/components/onboarding/completion-stream.spec.ts covers SSE parsing: content, chunk-boundary splits, malformed chunks, and an error arriving after partial content.

Screenshots

/onboarding after clicking Send request.

Before — the call never reaches the gateway.

Onboarding before, light theme: Request failed

Onboarding before, dark theme: Request failed

After — a real streamed answer, on a brand new account with no credits.

Onboarding after, light theme: streamed model response

Onboarding after, dark theme: streamed model response

Deploy note

Set ONBOARDING_CHAT_API_KEY to a platform-owned gateway key (same pattern as SUPPORT_CHAT_API_KEY, which it falls back to). Without it, onboarding's first call runs on the new account's own key and will 402 on a paid model.

Test plan

  • pnpm build ✅
  • pnpm vitest run apps/api/src/routes/chat.spec.ts apps/api/src/utils/playground-key.spec.ts packages/models packages/shared — 398 passed ✅
  • pnpm vitest run apps/gateway/src/api.spec.ts apps/gateway/src/dynamic-routes.spec.ts — 161 passed ✅
  • pnpm --filter ui test:e2e against a local stack — 2 passed ✅
  • Verified streaming still renders end to end through the typed client (real streamed answer in the browser, parseAs: "stream" confirmed not to consume the body)
  • Manually drove signup → onboarding → dashboard in the browser on an isolated stack; screenshots above are from that run.

🤖 Generated with Claude Code

Summary by CodeRabbit

  • New Features

    • New accounts can use sponsored chat requests during onboarding without providing an API key.
    • Onboarding requests use a consistent model and token limit, with sponsorship ending after onboarding completion or usage limits are reached.
    • Added clearer streaming error handling and improved onboarding progress controls.
  • Bug Fixes

    • Gateway URLs are normalized consistently, preventing duplicate version paths or trailing slashes.
  • Tests

    • Added coverage for authentication, sponsored requests, streaming responses, gateway handling, and browser-based onboarding scenarios.

The try-it request lost its session cookie when it moved to the API's
`/chat/completion` proxy, so every new account's first call 401'd. Send
credentials, normalize GATEWAY_URL's `/v1` suffix, and surface streamed
gateway errors instead of swallowing them as "No response received".

New accounts have no credits, so the proxy now sponsors that one call
with a platform key (`ONBOARDING_CHAT_API_KEY`), pinning the model and an
output cap server-side and only while onboarding is incomplete. The free
Claude Haiku 4.5 mapping it used to rely on is deactivated.

Adds a Playwright suite for apps/ui covering signup through onboarding.

Claude-Session: https://claude.ai/code/session_0174tWJnTyV1nd94FDDUAX1s
@coderabbitai

coderabbitai Bot commented Aug 10, 2026 •

Copy link
Copy Markdown
Contributor

Review Change Stack

Walkthrough

The PR adds authenticated onboarding chat sponsorship with a fixed model and token limit. It updates the onboarding UI and adds API, browser, stream-parser, and gateway URL tests. It also removes free-model snippet options and deactivates a retired provider.

Changes

Onboarding chat flow

Layer / File(s) Summary
Shared onboarding contract and API sponsorship
packages/shared/src/onboarding.ts, packages/shared/src/index.ts, apps/api/src/routes/chat.ts, apps/api/src/routes/chat.spec.ts
The API sponsors authenticated users who have not completed onboarding. Sponsored requests use the platform key, shared model, token limit, and source header. Redis limits sponsorship to five calls per 24-hour window. Other requests retain caller-key behavior.
Onboarding request and stream handling
apps/ui/src/components/onboarding/completion-stream.ts, apps/ui/src/components/onboarding/completion-stream.spec.ts, apps/ui/src/components/onboarding/onboarding-wizard.tsx, apps/ui/src/components/shared/quick-start-snippet.tsx
The wizard uses the typed client and shared model. Shared helpers parse SSE responses and report stream errors. Quick-start examples no longer set free_models_only.
Onboarding browser validation
apps/ui/e2e/onboarding.pw.ts, apps/ui/playwright.config.ts, apps/ui/package.json, apps/ui/tsconfig.json, apps/ui/.gitignore
Playwright coverage validates signup, API-key provisioning, sponsored requests, onboarding completion, dashboard navigation, and unauthenticated rejection.
Gateway and model support updates
apps/api/src/utils/playground-key.ts, apps/api/src/utils/playground-key.spec.ts, packages/models/src/models/anthropic.ts
Gateway URLs are normalized to one /v1 suffix. Tests cover configured and fallback URLs. The retired free Anthropic provider is marked deactivated.

Estimated code review effort: 4 (Complex) | ~45 minutes

Sequence Diagram(s)

sequenceDiagram
  participant User
  participant OnboardingWizard
  participant ChatRoute
  participant Redis
  participant Gateway
  User->>OnboardingWizard: Submit onboarding prompt
  OnboardingWizard->>ChatRoute: Send session-authenticated request
  ChatRoute->>Redis: Consume sponsorship allowance
  ChatRoute->>Gateway: Send sponsored completion
  Gateway-->>ChatRoute: Return SSE stream
  ChatRoute-->>OnboardingWizard: Forward completion stream
  OnboardingWizard-->>User: Render content or error
Loading

Possibly related PRs

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 25.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly identifies the main change: fixing the first onboarding API call.
✨ Finishing Touches 💡 1
📝 Generate docstrings 💡
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/onboarding-first-call

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 5

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
apps/ui/src/components/onboarding/onboarding-wizard.tsx (1)

126-148: 📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Use the generated API client for the onboarding chat proxy.

apps/ui/src/components/onboarding/onboarding-wizard.tsx:129 calls POST /chat/completion with raw fetch(). Use useFetchClient() and preserve the credentials: "include" setting and SSE/stream content/error handling the wizard already implements. The generated client does not need a new endpoint; it should call the existing proxy route through the API surface.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@apps/ui/src/components/onboarding/onboarding-wizard.tsx` around lines 126 -
148, The onboarding chat request in the wizard’s fetch flow should use the
generated API client via useFetchClient() instead of raw fetch to call the
existing chat completion proxy. Preserve credentials: "include", request payload
behavior, AbortSignal support, and the existing SSE stream parsing and error
handling.

Source: Coding guidelines

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@apps/api/src/routes/chat.spec.ts`:
- Around line 25-29: Update the test setup and fallback test around
createTestUser and getOnboardingApiKey to preserve both ONBOARDING_CHAT_API_KEY
and SUPPORT_CHAT_API_KEY. Stub or save each variable before tests, clear both
when exercising onboarding-key fallback, and restore their original values
automatically after each test so pre-existing environment configuration is not
lost.

In `@apps/api/src/routes/chat.ts`:
- Around line 66-97: Update the sponsorship flow in the chat route around the
sponsored calculation and before the gateway fetch to enforce a server-side
per-user or per-organization allowance. Atomically reserve or consume the
allowance before forwarding sponsored requests, deny sponsorship when it is
exhausted, and preserve the existing API-key requirement and onboarding
model/token limits for allowed requests.

In `@apps/api/src/utils/playground-key.spec.ts`:
- Around line 9-16: Update the afterEach cleanup to restore NODE_ENV using the
same delete-or-restore pattern as GATEWAY_URL: delete process.env.NODE_ENV when
previousNodeEnv is undefined; otherwise assign the saved value.

In `@apps/api/src/utils/playground-key.ts`:
- Line 18: Update the URL normalization expression in the playground-key utility
to remove all repeated terminal `/v1` suffixes, while preserving the final
single `/v1` suffix and existing trailing-slash handling. Add a regression test
covering `https://gateway.example/v1/v1` and verify it produces a URL ending in
exactly `/v1`.

In `@apps/ui/src/components/onboarding/onboarding-wizard.tsx`:
- Around line 219-224: Update the stream-error handling around the streamError
check to setTryError and capture onboarding_try_error whenever streamError
exists, regardless of accumulated content. Preserve partial output visibility,
and add coverage for content followed by an SSE error event to ensure
onboarding_try_success is not recorded.

---

Outside diff comments:
In `@apps/ui/src/components/onboarding/onboarding-wizard.tsx`:
- Around line 126-148: The onboarding chat request in the wizard’s fetch flow
should use the generated API client via useFetchClient() instead of raw fetch to
call the existing chat completion proxy. Preserve credentials: "include",
request payload behavior, AbortSignal support, and the existing SSE stream
parsing and error handling.
🪄 Autofix

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro Plus

Run ID: fbe66d90-ea5d-453d-9600-2344bfb6a75d

📥 Commits

Reviewing files that changed from the base of the PR and between cd835f8 and 9252dad.

⛔ Files ignored due to path filters (1)
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (14)
  • apps/api/src/routes/chat.spec.ts
  • apps/api/src/routes/chat.ts
  • apps/api/src/utils/playground-key.spec.ts
  • apps/api/src/utils/playground-key.ts
  • apps/ui/.gitignore
  • apps/ui/e2e/onboarding.pw.ts
  • apps/ui/package.json
  • apps/ui/playwright.config.ts
  • apps/ui/src/components/onboarding/onboarding-wizard.tsx
  • apps/ui/src/components/shared/quick-start-snippet.tsx
  • apps/ui/tsconfig.json
  • packages/models/src/models/anthropic.ts
  • packages/shared/src/index.ts
  • packages/shared/src/onboarding.ts
💤 Files with no reviewable changes (1)
  • apps/ui/src/components/shared/quick-start-snippet.tsx

Comment thread apps/api/src/routes/chat.spec.ts
Comment thread apps/api/src/routes/chat.ts
Comment thread apps/api/src/utils/playground-key.spec.ts
Comment thread apps/api/src/utils/playground-key.ts Outdated
Comment thread apps/ui/src/components/onboarding/onboarding-wizard.tsx Outdated
Sponsored onboarding calls now consume a per-user Redis allowance, so an
account that never finishes onboarding can't loop on the platform key.
Collapse repeated `/v1` suffixes in getGatewayUrl, and restore NODE_ENV
and both platform-key env vars in the specs instead of clobbering them.

The wizard now calls the proxy through the generated typed client
(parseAs: "stream" keeps the SSE body unread) and reports a mid-stream
error even when partial content arrived, so a failed call is never
recorded as a success. Stream parsing moves to a tested helper.

Claude-Session: https://claude.ai/code/session_0174tWJnTyV1nd94FDDUAX1s

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (1)
apps/ui/src/components/onboarding/completion-stream.ts (1)

72-84: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Type the parsed SSE chunk instead of relying on the implicit any from JSON.parse.

JSON.parse returns any, so parsed.error, parsed.choices, and parsed.model are unchecked. A non-string model value would flow into CompletionStreamResult.model and then into the PostHog onboarding_try_success event in apps/ui/src/components/onboarding/onboarding-wizard.tsx at Line 186. A narrow local type keeps the reader strict without adding validation cost.

♻️ Proposed typed chunk shape
+interface CompletionChunk {
+	error?: unknown;
+	model?: unknown;
+	choices?: { delta?: { content?: unknown } }[];
+}
+
 			try {
-				const parsed = JSON.parse(data);
+				const parsed = JSON.parse(data) as CompletionChunk;
 				if (parsed.error) {
 					error = extractErrorMessage(parsed);
 				}
 				const delta = parsed.choices?.[0]?.delta?.content;
-				if (delta) {
+				if (typeof delta === "string" && delta) {
 					content += delta;
 					onContent(content);
 				}
-				if (parsed.model) {
+				if (typeof parsed.model === "string") {
 					model = parsed.model;
 				}
 			} catch {

As per coding guidelines: "Use strict TypeScript; never use any or as any unless absolutely necessary".

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@apps/ui/src/components/onboarding/completion-stream.ts` around lines 72 - 84,
Define a narrow local TypeScript type for the parsed SSE chunk, including the
optional error, choices/delta content, and string model fields, and parse JSON
into that type instead of relying on JSON.parse’s implicit any. Use the typed
value throughout this try block so parsed.error, parsed.choices, and
parsed.model remain strictly checked before updating completion state.

Source: Coding guidelines

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In `@apps/ui/src/components/onboarding/completion-stream.ts`:
- Around line 72-84: Define a narrow local TypeScript type for the parsed SSE
chunk, including the optional error, choices/delta content, and string model
fields, and parse JSON into that type instead of relying on JSON.parse’s
implicit any. Use the typed value throughout this try block so parsed.error,
parsed.choices, and parsed.model remain strictly checked before updating
completion state.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 95bba8e0-b324-4fdd-81f9-d0a9ca550ae0

📥 Commits

Reviewing files that changed from the base of the PR and between 9252dad and 97e5a8c.

📒 Files selected for processing (7)
  • apps/api/src/routes/chat.spec.ts
  • apps/api/src/routes/chat.ts
  • apps/api/src/utils/playground-key.spec.ts
  • apps/api/src/utils/playground-key.ts
  • apps/ui/src/components/onboarding/completion-stream.spec.ts
  • apps/ui/src/components/onboarding/completion-stream.ts
  • apps/ui/src/components/onboarding/onboarding-wizard.tsx
🚧 Files skipped from review as they are similar to previous changes (4)
  • apps/api/src/utils/playground-key.ts
  • apps/api/src/utils/playground-key.spec.ts
  • apps/api/src/routes/chat.ts
  • apps/api/src/routes/chat.spec.ts

@smakosh
smakosh enabled auto-merge (squash) August 10, 2026 18:54
@smakosh
smakosh merged commit a6eb013 into main Aug 10, 2026
25 checks passed
@smakosh
smakosh deleted the fix/onboarding-first-call branch August 10, 2026 19:13
smakosh added a commit that referenced this pull request Aug 11, 2026
## Problem

The post-signup onboarding wizard's "try it now" request does not show
up in the account's own activity log. The user sends a real request,
sees a real streamed answer, and then finds an empty dashboard.

`#3524` sponsored that first call with a platform-owned key
(`ONBOARDING_CHAT_API_KEY`), because a brand new organization is created
with 0 credits and a paid model would `402`. But the gateway derives
*everything* about ownership from the authenticating key —
`apps/gateway/src/chat/chat.ts` writes every log row with `projectId:
apiKey.projectId` and `organizationId: project.organizationId`. So the
row landed in the platform org's project, and `GET /logs` can never
return it: it scopes to `getActiveUserOrganizationIds(user.id)`.

This is deterministic, not a race or a worker delay — it happens on
every signup wherever the platform key is configured. In production the
missing rows are all sitting in the org that owns that key, filterable
with `source=onboarding`.

Ruled out along the way:

- **Retention.** New orgs default to `retentionLevel: "none"`, but
`insertLog` only strips payload fields in that case — the row is still
inserted.
- **Wrong project for the displayed key.** `/playground/ensure-key`
creates the key in the user's own default project.
- **Email verification.** `onboarding: true` already skips the
free-model gate.

## Approach

**The call now always runs on the account's own API key**, so logs,
usage and billing attribute correctly by construction rather than being
reconstructed after the fact.

That leaves the original problem: a new org has 0 credits. Rather than
paying for the call with a platform key (which is what broke
attribution) or granting credits, **that one call is zero-rated** — the
credit gate lets it through and the computed cost is zeroed, so nothing
is ever debited and no credits change hands.

Zero-rating rather than granting is deliberate. Credits are fungible and
bankable: a grant is worth its face value to a signup farmer, can be
spent on anything, and lands in every credit metric (`totalToppedUp` /
`unusedCredits` sum all completed transactions). A zero-rated call is
one capped completion that cannot be saved, redirected or spent on
anything else.

### How it is authorized

Eligibility is decided in the API proxy, where the session lives —
signed in, onboarding not yet complete, allowance not spent — and
asserted to the gateway with a shared secret.

The `onboarding` body flag deliberately cannot authorize this: it is
client-supplied, and honoring it would let anyone waive their own
charges.

### Notable details

- **Costs are zeroed in one wrapper** around `calculateCosts`, not at
each of the ~11 places costs are computed (streaming, non-streaming,
cached, cancelled, content-filter, error paths). The import is renamed
to `_calculateCosts`, so any billing path outside that wrapper's scope
**fails to compile** instead of silently charging the user for the first
thing they ever did.
- **Storage cost needs no special handling**: `calculateDataStorageCost`
returns `"0"` when `retentionLevel === "none"`, which is the default for
a new org. Nothing debits it, so the balance stays at exactly 0 rather
than going negative.
- **The allowance is reserved before the call and refunded when the
gateway never served it**, so a flaky provider can't exhaust onboarding
and leave the user on a bare `402`. The refund is guarded with a Lua
`exists` check — a bare `DECR` on an expired counter would recreate it
at `-1` with no TTL and hand out an extra call next window.
- **Onboarding runs on `"auto"`** so it never pins a model that later
gets retired or repriced.
- Without the secret configured (self-hosted, local dev) nothing is
zero-rated and the call is billed normally — it degrades to pre-`#3524`
behavior rather than silently giving work away.

## Regression cover

The Playwright suite now asserts **the thing that was actually broken**:
after the first call it polls `/logs?source=onboarding` until the
request appears in the new account's own activity. Since `/logs` only
ever returns the caller's own organizations, that assertion is precisely
the regression — it fails against the platform-key implementation.

- `apps/gateway/src/lib/onboarding-sponsorship.spec.ts` — the secret
check, weighted to the negative cases: no header, wrong secret, a prefix
of the secret (`timingSafeEqual` throws on length mismatch), and nothing
configured.
- `apps/api/src/routes/chat.spec.ts` — the request always leaves on the
caller's own key; sponsorship is asserted only while eligible; it stops
after onboarding completes, when no secret is set, and once the
allowance is spent; a failed gateway call refunds the allowance.

## Test plan

- `pnpm build` ✅
- `pnpm format` ✅
- `pnpm test:unit` against an isolated worktree stack — **4449 passed**,
2 skipped ✅
- One unrelated pre-existing failure in
`apps/gateway/src/chat/tools/openai-content-filter.spec.ts` ("logs
missing moderation credentials"), confirmed by reverting this branch's
gateway changes and reproducing it identically. It is sensitive to
whether `LLM_OPENAI_API_KEY` is present locally.
- `pnpm --filter ui test:e2e` requires the worker running, since log
rows reach the database through the log queue.

## Deploy note

Set **`ONBOARDING_SPONSOR_SECRET`** to a random secret shared by the API
and the gateway. Without it, onboarding's first call is billed to the
new account and will `402`.

**`ONBOARDING_CHAT_API_KEY`** (added by `#3524`) is no longer read
anywhere and can be removed from the deploy config.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit

* **New Features**
* Eligible onboarding requests can proceed without consuming account
credits when authorized by the configured sponsorship secret.
* Onboarding usage remains recorded in account activity with zero
inference and storage costs.
* Onboarding now uses the pinned DeepSeek model, with a 4,000-character
prompt limit and 512-token output limit.

* **Bug Fixes**
* Failed, canceled, streaming, cached, malformed, and filtered requests
no longer consume sponsorship allowances.
* Sponsorship requires the caller’s API key and server authorization;
the client-provided onboarding flag no longer grants access.
* Normal plan, model, and free-model restrictions remain enforced for
sponsored requests.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant