feat(api-access): open API access to all users, sell Plus on limits - #6642
feat(api-access): open API access to all users, sell Plus on limits#6642capJavert wants to merge 3 commits into
Conversation
Personal Access Tokens no longer require Plus (dailydotdev/daily-api#4262). Any confirmed account can mint one, so the settings page stops gating Create behind a subscription and four surfaces stop selling Plus as "Get API Access". The page does no eligibility pre-check. The API is the single source of truth on who may mint a token, so Create stays clickable and the server's refusal is surfaced in the toast, which previously swallowed every failure into a generic message. That keeps emailConfirmed out of the boot payload and lets this ship independently of the API change. Plus is now an optional upsell rather than a gate: a plus-entry-gradient card on the settings page and a trailing line in Ask, both selling full API access and higher rate limits. The Ask page and FAQ claimed a Plus subscription was required, which stops being true. No request counts in the UI copy. They live in daily-api and would drift here unnoticed. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
capJavert
left a comment
There was a problem hiding this comment.
Summary
Opens PAT creation to all users and repositions Plus copy around full API access and higher rate limits. Scope is clean, copy-only on most surfaces, and the settings-page gate removal correctly relies on the API as the single authority (no client pre-check). Cross-checked against daily-api#4262: the unconfirmed-account refusal is thrown as FORBIDDEN, so the new toast path lines up. No blocking findings; three non-blocking notes inline.
Reviewed by AI.
| }: Props): ReactElement | null => { | ||
| const { isPlus, logSubscriptionEvent } = usePlusSubscription(); | ||
| const plusCta = 'Get API Access'; | ||
| const plusCta = 'Full API Access and higher rate limits'; |
There was a problem hiding this comment.
Non-blocking (copy drift + duplication): This surface already diverges from the others — "Full API Access" (title case) vs "Full API access" everywhere else, and the tooltip renders it with an appended ! ("Full API Access and higher rate limits!") where the old copy was a verb-led CTA ("Get API Access!"). The same CTA string is now hand-written in PlusUserBadge, UpgradeToPlus, MainSection, and ProfilePanelSection; one has already drifted within this PR. Consider a single exported constant so the next copy retune changes one place.
Reviewed by AI.
There was a problem hiding this comment.
Fixed in 5319b07. Exported plusApiCta / plusApiCtaShort from lib/constants and pointed every site at them — the four CTA surfaces you named plus the two card headings in settings/api.tsx and agents/ask/index.tsx, which repeated the same sentence. A grep now finds one literal, the constant itself.
Also confirmed the !: it was rendered as {plusCta}!, so the tooltip read "Full API Access and higher rate limits!". Dropped along with the title-case "Access".
| const ctaCopy = { | ||
| full: 'Full API access and higher rate limits', | ||
| short: 'Full API access', | ||
| }; |
There was a problem hiding this comment.
Non-blocking (layout verification): full grows from 14 to 38 characters and this button renders in constrained header/sidebar slots. isFullCTAText shows the long form on mobile (!isLaptop) and laptopXL — mobile is exactly where a 38-char label can wrap or overflow next to the sale label. A screenshot at mobile and laptopXL widths (with isSaleActive on) would settle it. Same applies to the MainSection sidebar copy.
Reviewed by AI.
There was a problem hiding this comment.
Not changed — flagging rather than silently skipping.
The length increase is in the copy itself (Get API Access 14 chars → 38), which predates this review round; the commit here only moves those strings behind a constant and changes no rendered text. So this needs eyes rather than a code fix.
Worth noting the sharpest case is not this button but MainSection.tsx, which uses ctaCopy.full unconditionally — no short variant at any width — in a fixed-width sidebar row, with PlusSaleLabel optionally pinned right. UpgradeToPlus at least falls back to the short form in the 1020–1668px band; below and above that it shows the long one.
| displayToast('Failed to create token. Please try again.'); | ||
| } catch (err) { | ||
| displayToast( | ||
| getApiError(err as ApiErrorResult, ApiError.Forbidden)?.message ?? |
There was a problem hiding this comment.
Non-blocking (question, interim state): Since the two PRs can merge in either order — if this merges before daily-api#4262, a non-Plus user's create attempt hits the current Plus gate. Worth confirming the current API refusal is also thrown as FORBIDDEN with user-presentable text, since this toast now surfaces the raw server message verbatim. Also intentional per the description, but noting: non-Forbidden refusals (e.g. RATE_LIMITED, CONFLICT on duplicate name if the API adds one) still collapse into the generic fallback.
Reviewed by AI.
There was a problem hiding this comment.
Confirmed on the interim state: the current gate throws ForbiddenError("API access requires an active Plus subscription"), so it is FORBIDDEN with user-presentable text and the toast reads correctly if this merges first.
The second half was a real find, fixed in 5319b07. Matching only FORBIDDEN dropped the token-limit message — it is an apollo ValidationError, which the daily-api spec asserts arrives as GRAPHQL_VALIDATION_FAILED, so "Maximum of 5 tokens allowed per user" was being replaced by the generic fallback. The toast now reads the first error message regardless of code.
The CTA string was hand-written in six places and had already drifted within one PR: PlusUserBadge carried title-case "Access" and rendered a trailing "!" left over from the verb-led "Get API Access!", which reads oddly on a noun phrase. Both card headings repeated the sentence too. Export it once from lib/constants and point every site at it. The create-token toast now surfaces the first error regardless of code. Matching only FORBIDDEN dropped the token-limit message, which is an apollo ValidationError and so arrives as GRAPHQL_VALIDATION_FAILED - "Maximum of 5 tokens allowed per user" was being replaced with the generic fallback. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Companion to dailydotdev/daily-api#4262 (API side) and dailydotdev/daily#2148 (agent skills).
Personal Access Tokens no longer require Plus, so the settings page stops gating token creation and four surfaces stop selling Plus as API access. Plus is now pitched on full API access and higher rate limits.
Independent of the API PR — the client does no eligibility pre-check, so these can merge in either order.
Settings page
usePlusSubscriptionno longer gates the Create button or the empty state.emailConfirmedout of the boot payload and out ofLoggedUser. An earlier draft added it so the button could be pre-disabled; dropping it removed a cross-repo dependency for no real UX loss.plus-entry-gradient, the existing Plus-entry treatment (base.css:835), placed below the description so it never blocks the primary action.Ask
The page and FAQ both asserted a Plus subscription was required:
AskInstalldrops the requires-Plus branch and gains an optional trailing "Upgrade to Plus" for non-Plus users.Plus copy
UpgradeToPlus,MainSection,PlusUserBadge,ProfilePanelSectionand bothPlusListentries move from "Get API Access" to "Full API access and higher rate limits".ProfilePanelSection's purple row keeps its!isPluscondition,logSubscriptionEventcall andTargetId.ProfileDropdownuntouched — its comment notes it is the only upgrade entry point left on that panel, so only the label changed.Analytics
New
TargetId.ApiAccess, used by the settings card and the Ask inline link, so the new upsell surfaces are attributable rather than folded intoAccount.No numbers in UI copy
Request counts live in
daily-api's limiter constants. Earlier drafts repeated them here and in twoPlusListtooltips; they would drift silently on the next retune, so all UI copy is qualitative. The agent-facing skill still states them, next to theX-RateLimit-*headers.Verification
pnpm --filter @dailydotdev/shared lint,pnpm --filter webapp lintandnode ./scripts/typecheck-strict-changed.jsall clean.Full
pnpm run typecheckreports 24 errors andpnpm --filter webapp testone failure (WorldGuideSheet.spec.tsx) — both verified identical on a cleanmain, so pre-existing and untouched here.No tests cover
settings/api.tsx. Worth checking by hand: free confirmed user mints a token; unconfirmed user sees the API's own refusal in the toast rather than the generic fallback; Plus user unchanged.🤖 Generated with Claude Code
Preview domain
https://public-api-free-tier.preview.app.daily.dev