Skip to content

feat(api-access): open API access to all users, sell Plus on limits - #6642

Open
capJavert wants to merge 5 commits into
mainfrom
public-api-free-tier
Open

feat(api-access): open API access to all users, sell Plus on limits#6642
capJavert wants to merge 5 commits into
mainfrom
public-api-free-tier

Conversation

@capJavert

@capJavert capJavert commented Sep 10, 2026

Copy link
Copy Markdown
Contributor

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

  • usePlusSubscription no longer gates the Create button or the empty state.
  • No pre-check. Create stays clickable for everyone; the API decides who may mint a token. Its refusal is surfaced in the toast, which previously swallowed every failure into "Failed to create token. Please try again." — that would have hidden the unconfirmed-account message entirely. This is what replaces the gate, so it is load-bearing rather than polish.
  • Deliberately keeps emailConfirmed out of the boot payload and out of LoggedUser. An earlier draft added it so the button could be pre-disabled; dropping it removed a cross-repo dependency for no real UX loss.
  • Optional upsell card for non-Plus users using 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:

  • Ask page card: "Unlock daily-dev-ask with Plus / daily-dev-ask requires a Plus subscription" → now sells full API access and higher rate limits.
  • FAQ "Is this free?" answered that it is not. Now answers that any account can use it.
  • AskInstall drops the requires-Plus branch and gains an optional trailing "Upgrade to Plus" for non-Plus users.

Plus copy

UpgradeToPlus, MainSection, PlusUserBadge, ProfilePanelSection and both PlusList entries move from "Get API Access" to "Full API access and higher rate limits".

ProfilePanelSection's purple row keeps its !isPlus condition, logSubscriptionEvent call and TargetId.ProfileDropdown untouched — 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 into Account.

No numbers in UI copy

Request counts live in daily-api's limiter constants. Earlier drafts repeated them here and in two PlusList tooltips; they would drift silently on the next retune, so all UI copy is qualitative. The agent-facing skill still states them, next to the X-RateLimit-* headers.

Verification

pnpm --filter @dailydotdev/shared lint, pnpm --filter webapp lint and node ./scripts/typecheck-strict-changed.js all clean.

Full pnpm run typecheck reports 24 errors and pnpm --filter webapp test one failure (WorldGuideSheet.spec.tsx) — both verified identical on a clean main, 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

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>
@vercel

vercel Bot commented Sep 10, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated
daily-webapp Ready Ready Preview Sep 11, 2026 12:48pm UTC

Request Review

@capJavert capJavert left a comment

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

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';

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

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.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

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',
};

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

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.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

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.

Comment thread packages/webapp/pages/settings/api.tsx Outdated
displayToast('Failed to create token. Please try again.');
} catch (err) {
displayToast(
getApiError(err as ApiErrorResult, ApiError.Forbidden)?.message ??

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

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.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

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.

capJavert and others added 2 commits September 11, 2026 09:29
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>
The five API quest event types land in both destination maps so their
cards link somewhere useful. `questDestinations` is shared by the daily
and weekly renderers; intro keeps its own map.

Daily rotations are global with no per-user eligibility, so a user with
no token will be served an API quest they cannot complete. The
destination is what makes that acceptable: the card becomes a route into
token creation rather than a dead slot.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The intro quest became "fetch your profile through the API" so the
publisher fires only on mapped routes instead of once per API request.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
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