项目文件夹

文件
wehub-resource-sync 98e40dac97
CLI Smoke Test / smoke-test-linux (20) (push) Has been cancelled
CLI Smoke Test / smoke-test-linux (24) (push) Has been cancelled
CLI Smoke Test / smoke-test-windows (20) (push) Has been cancelled
CLI Smoke Test / smoke-test-windows (24) (push) Has been cancelled
Expo App TypeScript typecheck / typecheck (push) Has been cancelled
chore: import upstream snapshot with attribution
2026-07-13 12:40:49 +08:00

3.3 KiB

Paid Voice — Rate Limiting & Auth

Flow

User taps mic
│
├─ Bypass mode? (custom agent ID)
│   └─ connect directly to ElevenLabs, skip everything
│
├─ POST /v1/voice/conversations { agentId }
│   │
│   ├─ GET /v1/convai/conversations?agent_id=X&user_id=Y&created_after=<30d>&page_size=100
│   │   └─ Sum call_duration_secs → usedSeconds (~108ms)
│   │
│   ├─ conversations == 100?          → { allowed: false, reason: "voice_conversation_limit_reached" }
│   ├─ usedSeconds >= 5h?             → { allowed: false, reason: "voice_hard_limit_reached" }
│   ├─ usedSeconds >= 20min + no sub? → { allowed: false, reason: "subscription_required" }
│   │
│   ├─ GET /v1/convai/conversation/token?agent_id=X&participant_name=ELEVEN_USER_ID
│   │   └─ Decode JWT → extract conv_id from video.room
│   │
│   └─ Return { conversationToken, conversationId, agentId, elevenUserId, usedSeconds, limitSeconds }
│
├─ allowed: false?
│   ├─ "voice_conversation_limit_reached" → alert (file issue on GitHub)
│   └─ other → paywall flow="voice_must_pay"
│
└─ allowed: true
    ├─ feature flag voice-upsell == "show-paywall-before-first-voice-chat"?
    │   └─ first free voice start only → soft paywall flow="voice_trial_eligible"
    ├─ feature flag voice-upsell == "voice-onboarding-and-upsell"?
    │   └─ inject onboarding + upsell guidance into voice prompt
    └─ otherwise
        └─ control → no soft paywall and no onboarding experiment
        then startSession({ conversationToken }) → WebRTC via LiveKit

Limits

Tier Limit Window Cost to us What happens
Free 20 min 30 days ~$0.19 Paywall
Subscribed 5 hours 30 days Hard block → BYO agent
BYO Agent Unlimited $0 User's own ElevenLabs
Any 100 conversations 30 days Hard block → file issue

Cost: ~$0.01/min ($1600 / 171K min measured).

Tracking

ElevenLabs is the source of truth. No local DB.

  • participant_name on token mint → sets user_id on conversation record
  • Usage: GET /conversations?user_id=Y&created_after=<30d>&page_size=100 → sum durations
  • user_id = HMAC-SHA256 of Happy user ID (deterministic, one-way)
  • Max page_size is 100 → at 100 conversations we block (can't track more without pagination)

TODO: Remove VoiceConversation model from Prisma schema (no longer used, DB table can be dropped).

Paywall Flows (RevenueCat)

Single paywall template, rules driven by custom variable flow:

Flow When Behavior
voice_trial_eligible Feature flag variant show-paywall-before-first-voice-chat, first free voice use Soft — dismissable, voice starts anyway
voice_must_pay Server returns allowed: false Hard — must purchase
voluntary_support Settings User-initiated

Future: Voice Agent Self-Sell

Have the agent mention pricing naturally. Inject usedSeconds/limitSeconds into context, add showUpgradePaywall client tool.

Security

  • JWT signed by ElevenLabs, single-use, can't be forged
  • Agent set to "authorized only" — needs server-minted token
  • Agent ID in public repo is harmless