TryWordy

Rails 8 + Nuxt 4 · Claude · ElevenLabs

A calm vocabulary tool built solo, from design to deploy — Claude generates the cards, ElevenLabs speaks them, a Telegram bot drips daily reviews, and a Rails API keeps the cost of it all under control.

Visit trywordy.com
Role
Solo — product, design, frontend, backend, ops
Frontend
Nuxt 4 · Vue 3 · TypeScript on Vercel
Backend
Rails 8 API-only · PostgreSQL · Puma
AI
Claude Sonnet 4.5 (tool-use JSON) · ElevenLabs Multilingual v2
Daily coach
Telegram bot · spaced-repetition reviews in-chat
Billing & auth
Stripe Checkout · JWT multi-device sessions
Deploy
Kamal 2 to DigitalOcean · managed Postgres · zero-downtime

What it does

Paste a batch of words or phrases in any language and TryWordy returns clean vocabulary cards — definitions in both your target and native language, part of speech, register, synonyms and antonyms you would actually use, real example sentences, and natural pronunciation you can play back.

The whole product is built to feel like a well-made tool rather than a consumer app: no gamification, no mascots, no aggressive prompts. Just the lookup, done well.

Definitions: Claude, made reliable

The cards are generated by Claude Sonnet 4.5. Rather than parse free-form text, I have the model return its answer through a tool with a strict JSON schema, so every card has the same shape and a malformed response fails loudly instead of silently corrupting a card.

A single request covers up to twenty-five words and resolves every supported native language at once. The shared system prompt is marked for ephemeral caching, so repeated lookups within a short window reuse it at a heavy input-token discount — the kind of saving that only matters when one person is paying the bill.

Keeping a solo AI product affordable

An AI tool that anyone can hit is also a tool anyone can run up a bill on, so cost control is a first-class feature, not an afterthought.

Looked-up words are cached as dictionary entries; when several people request the same word at once, a Postgres advisory lock per word-and-language-pair lets exactly one request reach Claude while the rest wait for the result. On top of per-user quotas there is a global daily spend ceiling that scales with the number of paying users, so a bad day can't outrun the revenue. Email verification is required before any lookup runs, which closes the obvious abuse vector of farming disposable accounts to burn credits.

Pronunciation and the rest of the stack

Pronunciation audio comes from ElevenLabs' multilingual model, cached per word and voice so each clip is generated once and reused forever; the free tier falls back to the browser's built-in speech. Auth is JWT with per-device sessions so a logout or password change can revoke a single device. Billing runs through Stripe Checkout and the Customer Portal, with webhooks as the source of truth for which tier a user is on.

The Rails API ships to a DigitalOcean droplet with Kamal 2 for zero-downtime deploys, backed by a managed Postgres instance and Amazon SES for mail — with SES bounce and complaint notifications flowing back through an SNS webhook to suppress dead addresses automatically.

A daily coach that lives in Telegram

Looking words up is easy; coming back to review them is the hard part. So paying members can opt into a daily coach that sends a review straight to their Telegram, at the hour and on the cadence they choose — every day, weekdays only, or Mondays/Wednesdays/Fridays.

The whole review happens inside the chat. Each card shows the word, its pronunciation, definition and an example, with Again / Hard / Good / Easy buttons right there — no link back to the app, no separate reveal step. Tapping a grade advances that word's spaced-repetition schedule and records the review exactly as if it happened in the app, then edits the message in place. The bot reuses the same scheduling and word data the website already runs on, so progress is one shared picture whether you study in the browser or in the chat.

A small set of in-chat commands covers the rest:

  • Pull a review card on demand, any time
  • Check your mastered, learning and due counts
  • Skip just today without pausing the coach
  • Pause and resume whenever life gets busy

Built on the constraints of a bot

A Telegram bot can't message someone first, so opting in is a deliberate handshake: the app mints a single-use, fifteen-minute link token and hands back a deep link; tapping it lets the bot bind the chat, and only the verified webhook is ever allowed to record which chat belongs to which account. The connection is never exposed to the browser — the app only ever knows whether a member is linked, not how to reach them.

From inside the chat a small set of commands covers the rest — ask for a word on demand, check mastered/learning/due counts, skip a day, pause, or resume — and the daily send itself is driven by an hourly job that resolves each member's local time and only fires once per day per person, retrying cleanly if a send fails. It's the same theme as the rest of TryWordy: a feature that feels effortless to use, sitting on top of a fair amount of careful plumbing.

Why it matters

TryWordy is my proof of what zero-to-one-hundred looks like when I run the whole thing: the product decisions, the design, both halves of the codebase, and the operational details that decide whether an AI product is sustainable or just a demo.

Stack

Rails 8Nuxt 4PostgreSQLClaude APIElevenLabs TTSTelegram Bot API

← All selected work