Implementation recipes

173 recipes. Every one already written.

These are not feature bullets. They are the guides that ship inside the product, on the Implement tab of each module. Each one carries a goal, prerequisites, the full file inventory, ordered steps with real code, verification at every stage, and a definition of done. Below is what each one covers.

The recipes ship in every pack Code and steps unlock with a licence
Recipes
173
Modules covered
60
Advanced
43
Hours of work
151+

Showing 173 of 173 recipes.

Feature accessintermediate45 min

Gate SaaS features by plan and override

One entitlement decision your product code can ask anywhere: allow, deny, or a limit, resolved from plan defaults, global kill switches and per-org overrides an admin can change without a deploy. Enforcement happens server-side before anything is written, unknown keys deny by default, and the UI shows the same decision the server enforced.

reason strings the UI can show

Feature accessintermediate45 min

Roll out a feature to a percentage of orgs

Ship a feature to ten percent of tenants and keep it there. Membership in the canary is stable, so no customer sees a feature appear and vanish between requests, and you raise the percentage from the admin panel without a deploy. A single customer can still be force-enabled ahead of the rollout.

same tenant stays in the canary

Lemon Squeezyintermediate65 min

Ship Lemon Squeezy MoR checkout

Lemon Squeezy checkout, overlay or hosted, with Lemon Squeezy acting as merchant of record so tax and receipts are handled for you. The buying organization travels through checkout to signature-verified webhooks, so a completed purchase raises that workspace's Pro limits and a cancellation drops them back to free.

tying an anonymous checkout to the right org

Lemon Squeezyintermediate30 min

Verify Lemon Squeezy webhooks properly, then make them idempotent

A billing webhook without real signature checking is a public endpoint for granting yourself a paid plan. This closes that: signatures are checked in constant time over the exact bytes Lemon Squeezy signed, stale or malformed ones are refused, and a redelivered event lands as a recorded duplicate instead of a double-applied upgrade.

signing over the exact raw body

Lemon Squeezyintermediate35 min

Cover the subscription events that decide who keeps access

Created and cancelled are the easy two. This covers the rest: payment failures move a customer to past due instead of an instant downgrade, pauses and expiries change access when they should, and a cancelled subscription keeps what was paid for until the period actually ends. Every transition is tested against real payload shapes.

cancelled but still paid through period end

Lemon Squeezyintermediate35 min

Issue and validate license keys for one-time purchases

Sell a lifetime deal, a plugin or a desktop app through Lemon Squeezy: each purchase issues a license key tied to the buying organization, a public endpoint validates and activates it, and per-device activation limits are enforced. Buyers see their activated devices and can free a slot on their own.

freeing a device slot without killing the key

Lemon Squeezyintermediate30 min

Repair billing state when a webhook never arrives

Webhooks get lost to deploys, timeouts and exhausted retries. A nightly sweep pages through every Lemon Squeezy subscription, compares each against your own records, repairs any drift it finds, and reports how many rows it corrected, so a lost event becomes a logged repair instead of a quiet billing mystery.

paying customers silently missing their access

Lemon Squeezybeginner25 min

Let customers change plan and update their card themselves

Customers change plan and update their card themselves, from one panel in your billing page. Plan changes go through Lemon Squeezy with an explicit proration choice, portal links for card updates and cancellation are signed, short-lived and minted fresh on each click, and the resulting webhook keeps your entitlements in step.

expiring portal links pasted into emails

Paddle billingintermediate70 min

Sell Pro with Paddle Billing

Sell Pro through Paddle as merchant of record, so tax and invoicing are their problem. Checkout opens as an overlay in your app, the buying organization is carried through the transaction, and verified webhooks turn an activated subscription into raised limits and a cancellation back into the free plan, proven end to end in sandbox.

sandbox events proving the entitlement path

Paddle billingintermediate35 min

Handle Paddle’s full subscription event lifecycle

Paddle sends far more than activated and canceled. Trialing, past due, paused and resumed each change what a customer is owed, and every one of them is mapped to the entitlements your product enforces. Retries are absorbed rather than double-applied, and a customer who pauses or resumes sees access change within one webhook.

paused and resumed subscriptions keeping entitlements honest

Paddle billingintermediate35 min

Change a customer's plan mid-cycle without surprising them

Mid-cycle plan changes on Paddle with the right proration mode: upgrades charged pro rata and applied at once, downgrades scheduled for period end with a stated effective date. Customers see a preview of the charge before confirming, and the resulting webhook keeps your records and their entitlements in step.

picking the correct proration mode

Paddle billingadvanced40 min

Recover failed payments before they become churn

Most declines are a stale expiry date, not a customer leaving. Past-due Paddle subscriptions keep working through a defined grace period while the customer sees escalating banners and emails that link straight to updating their card, and the downgrade happens automatically, exactly once, only when the window closes without a recovered payment.

grace period sized to recover, not to leak

Paddle billingintermediate30 min

Show correct local prices and tax on the pricing page

A merchant of record handles tax in every country, but that only reaches buyers if the pricing page shows it. Visitors see their own currency with tax presented the way their country expects, results are cached per country to stay inside Paddle's rate limits, and base prices appear when the preview is unavailable.

per-country caching under preview rate limits

Paddle billingbeginner25 min

Go live on Paddle without mixing sandbox and production

Most Paddle launch incidents are one id still pointing at sandbox. Here environment selection comes from a single source that cannot be half-switched, price ids resolve per environment, a preflight check fails loudly on any mismatch, and a written cutover order covers webhook registration and replaying events from launch day.

sandbox ids surviving into the live deploy

Polar billingintermediate75 min

Charge for the Pro plan with Polar

Sell a Pro subscription through Polar and have the purchase raise that organization's limits on its own. Polar's Convex component owns checkout, subscription state and webhook ingestion, so your product code stays unchanged, an upgrade page shows the live plan, and a cancellation restores free limits as automatically as the upgrade granted them.

checkout to entitlement with zero product edits

Polar billingintermediate90 min

Bill per seat and stop invites at the cap

Enforce paid seat limits when an invitation is created: pending invitations count toward the cap, and removing a member immediately lowers the provider’s billed quantity. The plan card shows seats used against seats paid for.

billed quantity drifting from real membership

Polar billingintermediate80 min

Run a 14-day trial with no card

Give new organizations full Pro limits for fourteen days with no card. A countdown banner appears in the final week and turns urgent near the end, expiry drops the organization back to free within the hour, a second trial for the same organization is refused, and anyone who upgraded mid-trial keeps their paid limits untouched.

refusing a second trial to the same org

Polar billingadvanced100 min

Upgrade now, downgrade at period end

Upgrades take effect immediately with a prorated charge; downgrades wait for the period boundary. Before anyone confirms, a preview compares their current usage against the smaller plan's limits and warns about anything that would no longer fit, and a scheduled downgrade stays visible with what is active today and what changes on which date.

customers sitting above the new plan's cap

Polar billingintermediate75 min

Cancel at period end, capture why, allow reactivation

Cancellation that keeps access until the paid period ends and records a structured reason for churn analysis. The save offer matches the stated reason, a cheaper plan for price, the roadmap for a missing feature, support for reliability, and a customer who changes their mind reactivates in one click any time before the boundary.

reactivation before the period boundary

Polar billingadvanced95 min

Survive a failed payment without losing the customer

A failed card should not end the relationship. Past-due organizations keep read access through a seven-day grace window while writes are held with a clear message, a three-email recovery sequence runs alongside an in-product banner, and full limits return on their own the moment a retry clears.

read-only grace instead of instant cutoff

Polar billingadvanced90 min

Make billing webhooks survive a bad day

Billing events your handler rejected are kept with their full payload and error instead of vanishing into provider retry limits. An operator replays them from the admin area once the fix ships, and a reconcile pass checks the provider's live subscription and repairs anything that drifted during the outage. Unverified events are still refused outright.

events lost while the deployment was down

Polar billingintermediate85 min

Pricing page and the paywall that points at it

A monthly and annual pricing page driven from one plan catalog, so what marketing shows and what the server grants cannot drift apart. The annual saving is computed rather than typed, discount codes survive into checkout, and a member who hits a limit sees the exact plan that raises it, one click from upgrading.

discount code surviving the checkout hop

Polar billingbeginner60 min

Invoice history, receipts, and a billing contact

Finance users get every past invoice with amount, status and date, plus downloadable receipts through short-lived provider links. Invoices are read live from the billing provider so a refund never leaves a stale copy behind, and a separate billing email and company address stop receipts landing in a founder's personal inbox.

short-lived receipt links, billing contact split from owner

Polar billingbeginner55 min

Decide who is allowed near billing

Money is not a normal permission. Members without billing rights lose the nav entry, the page and every server call behind it, while everyone can still see which plan the workspace is on. Each plan change is recorded with who made it and the plan before and after.

audit trail with before and after plan

Stripe Connectadvanced100 min

Onboard sellers with Connect Accounts v2

Seller onboarding on Stripe Connect's current account model: sellers complete hosted onboarding, buyers pay through a destination charge that routes the money to the seller and takes your platform fee, and charging is held back until the seller's payout capability is actually active, so money never lands somewhere it cannot leave.

charging before payout capability is live

Stripe Connectadvanced40 min

Refund a marketplace charge and decide who eats the platform fee

A marketplace refund pulls money from the seller's balance by default, and the platform fee does not come back unless you say so. This settles that policy: full and partial refunds reverse the fee proportionally, the action sits behind a permission and a stated reason, and every refund records who issued it.

platform fee silently unreversed on every refund

Stripe Connectintermediate40 min

Track connected-account requirements before payouts start bouncing

Sellers who onboarded months ago can quietly become restricted when Stripe asks for new documents. Each seller's capability status is mirrored locally, the seller sees a banner naming what Stripe wants and the deadline, with one click into re-onboarding, and new charges for a restricted account are refused before a buyer ever enters a card.

first symptom is usually a bounced payout

Stripe Connectintermediate35 min

Show sellers their balance and handle payouts that bounce

Sellers see available balance, pending balance and the next payout date inside your product, with every payout event kept in a history that answers where is my money. A bounced payout marks the account, tells the seller what actually went wrong, and routes them straight to fixing their bank details.

failed payouts nobody notices until the seller does

Stripe Connectadvanced40 min

Handle chargebacks without guessing who pays

Chargebacks on marketplace charges land on the platform balance even though the seller kept the money. Each dispute is captured with its evidence deadline, liability goes to platform or seller by an explicit policy that debits the responsible party, sellers submit evidence through your product, and reminders fire before the window closes.

who eats the chargeback, decided in advance

Stripe Connectintermediate35 min

Test Stripe Connect end to end with seeded test accounts

Connect bugs show up as money in the wrong account. This recipe seeds test connected accounts in known requirement states, pins fee-split and reversal arithmetic — including the rounding cases — with unit tests, and drives onboarding, charge, refund and payout against Stripe test mode, so the whole flow is verifiable in CI without production keys.

fee and reversal maths pinned by tests

Stripe subscriptionsadvanced90 min

Ship Stripe Checkout + entitlement bridge

Stripe subscription billing wired end to end: hosted Checkout with tax collection, signature-verified webhooks, and the Customer Portal for self-serve changes. A price-to-plan mapping turns payments into entitlements, so a completed test purchase raises an organization's limits and a portal cancellation drops them back to the free plan.

price id mapped to real entitlements

Stripe subscriptionsadvanced90 min

Charge the right tax and collect VAT numbers

Stripe Tax configured properly: billing addresses and VAT numbers are collected and validated at Checkout, so an EU business is zero-rated under reverse charge while a consumer on the same price pays local VAT. The applied tax is recorded for invoices, and a threshold monitor flags the next country before you owe registration there.

registration thresholds before you owe money abroad

Stripe subscriptionsintermediate70 min

Simulate a year of billing in a minute

Prove the billing path against real Stripe events instead of hand-written fixtures. A scripted customer on a Stripe test clock runs through trial expiry, renewals and failed payments in seconds, with entitlements asserted after every advance, and the rehearsal is repeatable enough to run before each release.

a year of renewals in under a minute

Stripe subscriptionsadvanced85 min

Accept more than cards without granting access too early

Wallets, Link, ACH and SEPA accepted alongside cards, with new methods enabled from the Stripe dashboard rather than a deployment. Card buyers get access within seconds, while delayed-settlement payments sit in a visible pending state and become entitlements only once the money actually clears.

SEPA clearing days after checkout completes

Stripe subscriptionsadvanced80 min

Handle a renewal that needs the customer's bank

European renewals that need bank authentication no longer look like churn. When an off-session charge asks for 3D Secure, the customer gets an email and an in-product prompt linking to Stripe's hosted invoice page, keeps access for a bounded 72-hour window, and drops into ordinary dunning only if they never authenticate.

off-session renewals that require 3D Secure

Stripe subscriptionsintermediate65 min

Go live without crossing test and live data

Test and live Stripe data stay apart. Each deployment carries its own restricted key, webhook secret and endpoint, a startup check refuses to boot billing when a live key meets a test price, and the go-live sequence — Radar and tax prerequisites included — is written down so the first real charge is uneventful.

live key paired with a test price id

Stripe subscriptionsadvanced100 min

Bill for usage with Stripe meters

Usage-based pricing where the invoice and the in-product usage page agree. Recorded usage flows to Stripe billing meters within the minute, prices in tiers with a free allowance, and shows a projected charge next to the numbers customers see. A nightly reconciliation names any organization whose Stripe total drifts from your own.

Stripe's meter total drifting from yours

Stripe subscriptionsintermediate70 min

Define retry policy and portal permissions as code

Retry policy and customer self-serve rights become reviewable code rather than settings someone once clicked in a dashboard. The portal grants plan switching and cancellation while withholding seat quantity changes, failed renewals retry four times over three weeks before going unpaid, and every portal session is pinned to the managed configuration.

portal permissions drifting from what was agreed

Stripe subscriptionsintermediate75 min

One-off charges, credit notes, and refunds

One-off charges land on the customer's next subscription invoice instead of a separate receipt, mid-period downgrades issue a credit note for the unused portion rather than cash, and a full refund drops the organization to free limits with an audit entry naming the operator — so support already knows what a refund does to access.

credit note versus cash refund on downgrade

Stripe subscriptionsadvanced80 min

Idempotency keys, a pinned API version, and rate limits

Every Stripe write goes through one client with the API version pinned, an idempotency key derived from the organization and intent, and backoff for rate limits and lock contention. A test replays a timed-out action and proves it produces one charge, never two — and a dashboard upgrade can no longer reshape your webhooks unreviewed.

a timed-out retry charging the customer twice

Usage meteringintermediate60 min

Replace collect() counting with an O(log n) aggregate

Plan limit checks that stay fast as tenants grow: creating a project reads a maintained per-organization count in logarithmic time instead of scanning every row. The count is kept in step with writes transactionally, and a backfill migration covers existing data and proves the maintained total equals the real one.

keeping the count exact under concurrent writes

Usage meteringadvanced55 min

Absorb hot meters with a sharded counter

High-frequency meters stop contending on a single document. Metrics you declare hot spread their increments across shards so hundreds of concurrent writes all land without colliding, quieter meters keep their exact per-user rows, and the usage page reads both kinds in the same view. Billing still settles on exact event totals.

write contention on one hot document

Usage meteringadvanced50 min

Count exactly once when writes retry

Retries, double-clicked buttons and redelivered webhooks each count once. Every metered event carries a dedupe claim taken in the same transaction as the increment, so a replay is recognized and reported as a duplicate without touching any total. Expired claims are swept nightly, keeping the dedupe table bounded.

sweeping expired claims so the table stays bounded

Usage meteringintermediate55 min

Monthly rollups, retention, and a usage trend chart

Twelve months of usage rendered without scanning a year of rows. A resumable nightly job folds daily records into one monthly total per organization and metric, raw rows are pruned once they pass the ninety-day retention window, and the trend chart on the usage page stays complete even for months with no activity.

zero-filled gaps instead of missing bars

Usage meteringadvanced60 min

Meter seats across the Better Auth boundary

Members live inside Better Auth, so seats cannot be counted like a local table. Each organization carries a mirrored seat count that makes the check cheap, an invite past the plan's seat entitlement is refused before the invitation ever sends, and a nightly reconcile corrects any row that drifted from the real member list.

counting seats across a component boundary

Usage-based billingintermediate40 min

Meter and bill document exports

Take an existing feature — document exports — and make it billable. One meter declaration drives everything: a daily included allowance, a quota gate that warns the user before it refuses the request, usage-page bars, and closed billing windows that report overage to your payment provider with the invoice math already settled.

warning at eighty percent, blocking at the cap

Account securityintermediate45 min

Production account recovery with session hygiene

A password change should end every other session, and your tables should hold no credential material. This recipe walks the recovery path — a reset link that expires in an hour, a server-enforced password policy, a confirmation that never reveals whether an address has an account — and leaves both claims demonstrable to a security reviewer.

sessions surviving a password rotation

Anonymousintermediate45 min

Offer guest sessions for demos only

Let a prospect try the product before creating anything. Guest sessions come from the Better Auth anonymous plugin, gated by an explicit flag on both the interface and the server, so demos work when you want them and with the flags off — the production default — anonymous sign-in is refused outright on both sides.

guest access that cannot leak into production

BA Admin pluginintermediate45 min

Operate the Better Auth user directory

Support gets a real user directory: search and list accounts, ban an abuser, change a role, or impersonate a user to reproduce a bug exactly as they see it. Better Auth itself denies non-admins, and the admin powers stay separate from the roles your product hands out.

separating support powers from product roles

Email OTPintermediate45 min

Sign in with email OTP codes

Some users would rather type six digits than hunt for a link, especially on a shared machine. This adds a code tab to the sign-in page: request a code, type it in, get a full session with no password involved. Codes travel through your existing email pipeline and preview locally without Resend.

codes that work when the link opens elsewhere

Firebase Auth bridgeadvanced90 min

Add Firebase phone sign-in without breaking the isolate

Let people sign in with a phone number Firebase has already verified — SMS code, bot check and all — and land them in an ordinary Better Auth session the rest of the app understands, with one user store and one session table. The Firebase Admin SDK stays fenced off where it cannot take sign-in down.

phone verification that cannot run in the isolate

Generic OAuthintermediate45 min

Connect a custom OAuth IdP

Enterprise buyers who insist on their own identity provider stop being a blocker. Point the app at any standards-compliant OAuth or OpenID Connect provider — Okta, Entra ID, or something homegrown — and their staff sign in through it into ordinary sessions. The provider button only appears once its configuration is complete.

one integration path for any compliant IdP

Google One Tapintermediate45 min

Add Google One Tap on marketing pages

Visitors on marketing and sign-in pages can be signed in from Google's One Tap prompt instead of a form. When the prompt is dismissed, blocked, or simply unavailable, the standard Google button takes over without an error in sight — and the whole feature stays off until its client id and flags are configured.

clean fallback when the prompt is refused

Instagram OAuthadvanced75 min

Add Instagram Business Login to the genericOAuth plugin

Add Instagram Business Login beside the providers you already offer. A visitor approves Meta's consent screen and lands on the dashboard as an ordinary account with a stable synthetic email — Instagram never supplies one — plus a stored access token ready for later Graph API calls. Covers the quirks: no discovery document, profile served separately.

a sign-in that hands you no email

Last login methodintermediate45 min

Highlight the last successful sign-in method

Once a product offers several ways in, the sign-in screen becomes a guessing game. This remembers which method succeeded last time and biases the sign-in page toward it on the next visit — the right tab or button already highlighted — so returning users take the same door they used before, no extra configuration needed.

steering returning users to the right method

Magic linkintermediate45 min

Passwordless sign-in with magic links

Nothing to remember and nothing to reset: a one-time email link signs the user straight into a full session. The link goes out through your existing transactional email pipeline and suits consumer products where password friction costs you sign-ups. Sending and accepting are both wired into the existing sign-in page.

single-use links and local preview without Resend

Multi-sessionintermediate45 min

Let users manage device sessions

Users can see every device currently signed in from a sessions panel in Settings and cut off the laptop they left behind. Each revoke removes exactly one session, and the browser they are sitting in stays signed in unless they revoke it themselves — no accidental self-logout.

revoking other devices, not the current one

Organizationsintermediate55 min

Run agency client workspaces

Agencies run each client as its own workspace, with billing, membership and deliverables hard-partitioned per client. Staff jump between clients from the shell without a reload, client contacts join as read-only viewers who can watch delivery in real time, and cross-client access is refused by the server, not just hidden by the interface.

client data isolation proven at the query layer

Organizationsadvanced75 min

Enforce seat-limited teams with an upgrade prompt

Seats become a real plan boundary. Invites are refused server-side once an organization uses every seat its plan allows, with pending invitations counted so nobody queues past the cap. The members screen shows seats used against the limit with an upgrade prompt, and support raises a customer's cap without shipping a release.

pending invites counted against the cap

Passkeysintermediate45 min

Offer passkey sign-in for returning users

Returning users sign in with the fingerprint or face unlock they already use. Passkeys are registered, listed and removed from account settings, so a lost device can be revoked without touching the account, and on supported browsers the password stops being the daily path.

device support gaps and revoking a lost passkey

Phone OTPintermediate45 min

Add phone OTP sign-in

Reach users who trust a phone number more than an inbox. Sign-in by text message code works end to end, with codes printed to the console in development so no SMS account is needed, and the same flow switches to a real provider such as Twilio for production.

developing OTP flows without paying per message

Referral codesintermediate90 min

Build referral attribution on the sign-in path you already have

Give every member a personal referral link, and credit the right referrer even when the invited person signs up days later or arrives through OAuth. Self-referrals and repeat sign-ups are refused, the referrer sees a live count on their dashboard, and the reward lands as an entitlement change rather than a number in a table.

attribution that survives a delayed signup

Settings & membersadvanced70 min

Transfer ownership and offboard members with guardrails

Founders leave, teams reshuffle, and the account survives it. Owners promote a successor and step down safely, removals and voluntary departures refuse to touch the last remaining owner, and every governance change is written to the audit log with actor and target, so no path leaves an organization ownerless.

never stranding an org without an owner

Sign-in & identityintermediate50 min

Launch a protected SaaS dashboard

Go from an empty deployment to a dashboard where every route demands a live Better Auth session and signed-out visitors are redirected to sign-in. The very first sign-up bootstraps its account and personal workspace in one pass, and you finish by adding a new protected page that proves the pattern.

first sign-in bootstrap, no half-built accounts

Sign-in & identityintermediate60 min

Run the password reset and email verification lifecycle

Locked-out users get themselves back in through reset links that expire after an hour, and the request page answers identically whether or not an account exists, so recovery cannot probe for registered emails. Reset mail is inspectable locally before Resend is wired up, and unverified accounts are refused by your riskiest actions server-side.

expiring reset tokens and unverified-email refusals

SIWEintermediate45 min

Sign in with Ethereum (SIWE)

Wallet holders authenticate by signing a message instead of creating another account, ending with a full session on your existing Better Auth setup. Each sign-in uses a fresh nonce, the signature is bound to your domain so a request from elsewhere is worthless, and the option stays hidden until configured.

domain binding that blocks cross-site replay

Social OAuthintermediate45 min

Enable Google and GitHub social sign-in

Google and GitHub sign-in buttons appear only once their credentials are actually configured, so a half-set-up environment never shows a path that fails on click. The OAuth callback lands users in a real session, and first-time arrivals get their account and workspace bootstrapped exactly like password sign-ups.

OAuth arrivals still get a workspace

Two-factor (MFA)intermediate45 min

Require TOTP on high-value accounts

For accounts where a stolen password would be expensive, add an authenticator-app challenge after the password step. Users enroll from settings, backup codes are issued once for the day the phone disappears, and a lost device becomes a self-service recovery instead of a support ticket.

recovery when the authenticator device is gone

Usernameintermediate45 min

Collect optional usernames at sign-up

Give people a handle to be known by without making it a second thing to lose. Sign-up accepts a username but never demands one, the profile shows it when present, email stays the recovery key, and a taken name comes back as a readable message rather than a raw error.

uniqueness conflicts surfaced as readable errors

Action Retrierintermediate45 min

Wrap a real provider call and settle state in onComplete

Provider calls settle their own state. A real email send runs under the retrier's supervision: its record starts as queued and moves to sent or failed exactly once, from the completion hook rather than the attempt itself, so nothing is marked delivered by an attempt that later failed and no record is stranded.

state settled outside the action body

Action Retrierintermediate45 min

Make a retried action safe to run twice

A timed-out attempt may already have reached the provider, and a naive retry sends twice. Stable idempotency keys, derived from the work itself rather than the clock, let a repeated attempt recognise the send already happened and return the original result, one delivery no matter how many attempts run.

timeouts that already reached the provider

Action Retrierintermediate45 min

Delay supervised work with runAfter and runAt

Supervised work can start after a delay or at an exact future timestamp instead of immediately. A provider's Retry-After cooldown gets one scheduled run rather than four attempts burned against a wall, deadlines that come from data fire on time, and a run still waiting reads as scheduled, not stuck.

honouring a Retry-After cooldown

Action Retrierintermediate45 min

Cancel a supervised run and reclaim its storage

Users get an abort control for supervised work they started, and cancellation settles through the same completion path as success and failure. A cancel that loses the race to an already-finished run is reported calmly rather than thrown, and settled run records are reclaimed on demand instead of lingering for a week.

reclaiming completed run storage

API keysintermediate70 min

Expose a public API secured by hashed keys

Customers generate a key in settings, see the secret exactly once, and call your public endpoints with a bearer token. The database stores only a hash and a display prefix, so a stolen backup yields no working credentials, and keys can be listed, rate limited and revoked per organization.

authenticating from a digest lookup

API keysintermediate55 min

Give each key only the permissions it needs

A key that inherits everything its creator can do is a full-account credential sitting in a config file. Every key instead carries an explicit scope list checked on each route, scope names stay stable for your docs, and creators can only grant scopes their own role already covers.

no privilege escalation through a key

API keysintermediate55 min

Expire keys on a schedule and rotate them without downtime

Keys carry an expiry date the customer picks, enforced on every request rather than by an overnight sweep. Rotation mints a linked successor while the old key stays valid through a grace window, owners are warned by email before the cutoff, and a report surfaces keys unused for ninety days.

overlap window so rotation never breaks

API keysintermediate50 min

Rate limit per key, bill per call, and return a 429 clients can obey

Per-key ceilings derived from the customer's plan stop one retry loop becoming everyone's outage, and keying by key means a runaway script cannot throttle its owner's other traffic. Every call is metered for billing, and responses carry the standard rate limit headers, so well-behaved SDKs back off on their own.

headers SDKs back off on by themselves

API keysintermediate50 min

Log every API request so customers can debug themselves

Customers debug themselves from their own request history: method, path, status and latency per key, with call volume, error rate and p95 latency for the last day. No column can hold a token or request body, every route is logged without exception, and old rows are swept on schedule.

logging requests without logging secrets

API keysadvanced55 min

Kill a leaked key automatically and give owners a panic button

Keys end up in public repositories, CI logs and screenshots. Secret-scanning reports in the format GitHub already sends revoke the matching key on arrival, owners are notified with an audit trail naming where it leaked, and admins hold an organization-wide revoke-everything button behind a fresh re-authentication.

matching a leaked secret to a stored hash

Automationsintermediate30 min

Make a new mutation automatable

Any mutation can become automatable, shown here by marking an invoice paid. That event fires customers' own when-this-then-that rules, email, signed webhook, agent or task, only after the write commits, the trigger appears as an option in the builder, and every run leaves a history summary a human can read.

trigger summaries a human can read

Durable workflowsintermediate45 min

Run a durable organization export

Long-running exports and reports survive restarts, and customers watch progress move from queued to complete live. Permission and plan checks run before anything is enqueued, so a denied organization leaves no half-created records, and you get durable background work without standing up a job runner of your own.

progress the UI can trust

Durable workflowsintermediate45 min

Add a custom durable SaaS job

Take the durable job pattern to any multi-step process: onboarding checklists, invoice PDFs, CRM syncs. Each new job keeps tenant isolation, reports its status to a live list in the UI, and sits behind a plan feature flag, so an expensive job can be switched off per customer without a deploy.

multi-step work that resumes mid-flight

Durable workflowsintermediate45 min

Handle workflow failure instead of leaving runs stuck

Failed and canceled jobs settle into a final state carrying the real error text, so a step that throws never leaves a customer watching a run sit at queued. The status badge renders failure and cancellation distinctly from work in progress, and every run terminates exactly once, whatever its outcome.

terminating a run exactly once

Durable workflowsintermediate45 min

Cancel a running workflow from the UI

A Cancel control that stops the engine itself rather than only the status row, so the record never lies about what is still running. The action is gated by the same permission that started the run, rejected server-side without it, and the button only appears while a run can still be stopped.

stopping the engine, not just the row

Durable workflowsintermediate45 min

Schedule a recurring durable job via cron

Nightly runs fan out across every organization still entitled to the feature, with no human clicking Start. The same durable job now serves system-triggered work, runs exist cleanly without a requesting user, and an organization whose plan or kill switch denies the feature gets zero runs, not a failed one.

skipping tenants who lost entitlement

Durable workflowsintermediate45 min

Notify the requester when a job finishes

Job finished, requester told. The person who clicked Start gets an in-app notification on success and a distinctly worded one on failure or cancellation, delivered through the same channel every other module uses, so mute preferences and duplicate suppression still apply, and nobody keeps a tab open watching a list.

different wording for the failure path

Error reportingintermediate45 min

Report errors with Sentry or console fallback

Exceptions reach Sentry in production tagged with release and source, and stay on the console locally when no DSN is configured. Events carry an organization tag for triage without any personal data, payloads never include secrets, and a built-in test capture proves the path before a real incident does.

keeping secrets out of error payloads

Error reportingintermediate45 min

Map ConvexError codes to user-facing UX

Denials become actionable screens — upgrade, ask an admin, sign in again — rather than raw stack traces. Feature, permission, plan-limit and session errors each get their own recovery path, unexpected failures still reach Sentry with full detail, and internal messages never leak to the customer.

a recovery path per denial reason

Outbound webhooksadvanced90 min

Deliver signed webhooks with retries and dead-letters

Your customers receive signed events within seconds of the product action that caused them. Endpoint registration, HMAC-signed payloads and a delivery queue come as one piece: failures retry with backoff up to eight attempts, dead deliveries stay visible with their last status code, and a slow receiver never slows your app.

dead letters visible to operators

Outbound webhooksintermediate55 min

Let customers manage endpoints and rotate signing secrets safely

Signing secrets cannot be hashed away like API keys, so rotation needs its own design. Customers add endpoints, choose event types, pause and rotate secrets from a self-service panel, every delivery carries signatures for both old and new secrets during the rotation window, and expired secrets are pruned automatically.

signing with two secrets at once

Outbound webhooksintermediate55 min

Ship the Recent deliveries panel customers debug from

The recent-deliveries panel customers debug from without filing a ticket. Every attempt keeps its own record — outgoing headers, response status, a bounded response snippet, timing, and connection failures marked distinctly — one click redelivers the original payload with a fresh signature, and a retention sweep stops the log growing forever.

keeping an attempt log from growing forever

Outbound webhooksadvanced55 min

Back off dead endpoints, disable them, and tell the customer

One abandoned customer URL can absorb queue capacity forever. Failing endpoints move through progressively longer cool-off periods, owners are warned in-product and by email before a sustained outage disables the endpoint with its last error named, and reactivating replays the deliveries missed during the outage within a bounded window.

replaying deliveries missed while down

Outbound webhooksadvanced50 min

Version your payloads so changing one cannot break subscribers

The first payload change after customers integrate is the one that breaks them. Events live in a typed catalog with one shape per version, endpoints stay pinned to the version they integrated against until the customer opts to upgrade, and a test ping exercises a new receiver before real events exist.

test pings before a real event exists

Outbound webhooksadvanced50 min

Stop your webhook sender being a proxy into your own network

A webhook sender posts wherever the customer says, handing every signup a request-forgery primitive. Private, loopback, link-local and cloud metadata addresses are refused at registration and re-checked at delivery time so DNS rebinding gains nothing, redirects are never followed, and every request is bounded by a timeout and a response size cap.

DNS rebinding between check and send

Rate Limiterintermediate45 min

Add a new named rate limit end to end

Protects an abuse-prone action end to end. You choose between token bucket and fixed window from the shape of the abuse, enforcement runs after authorization and before the write so anonymous traffic never touches limit state, and a denial reaches the user as a try-again countdown rather than a generic error.

where the check sits versus authorization

Rate Limiterintermediate45 min

Show remaining capacity before the user even tries

Users see remaining capacity before they click: the control disables itself, shows how many attempts are left, and counts down to the moment it re-enables — without polling. The hint corrects for client clock skew, each user can only read their own bucket, and the server keeps doing the enforcement that actually matters.

client hint, server stays the authority

Rate Limiterintermediate45 min

Reserve capacity instead of hard-denying background work

Background work reserves a future slot when the bucket is empty and schedules itself for the exact moment capacity frees up, so a burst spreads out instead of being dropped or retried blindly. Reservations are capped, and once they run out the caller gets honest backpressure with a wait time attached.

spreading a burst instead of retrying blind

Rate Limiterintermediate45 min

Let support clear a customer's rate limit

Support can clear one customer's throttle after a false positive, and the customer's very next request succeeds against a fresh bucket. Only an explicit allowlist of limits can be reset — abuse-containment limits stay off the table — behind fresh password step-up and a required reason written to the immutable audit trail.

step-up and reason on a support override

Workpoolintermediate50 min

Give every queued job a row users can watch

"Where is my export?" gets an answer on screen. Every queued job owns a record that moves from pending through running to succeeded, failed or canceled, carrying attempt counts and the real error text, and a live dashboard table shows each organization its own jobs without polling.

closing the row when the job dies

Workpooladvanced55 min

Fan out over 50,000 rows without blowing a transaction

Backfills across fifty thousand rows run as a self-resuming paginated driver that enqueues fixed-size batches into a capped pool, so a re-index or bulk send never blows a single transaction's budget. Progress persists between passes, survives interruption, and reads out as processed and remaining counts on the admin screen.

transaction budget on huge fan-outs

Workpoolintermediate45 min

Stop the queue growing faster than it can drain

Pools cap execution, not enqueueing, so a runaway producer can turn a five-second job into a four-hour wait. Each enqueue path gets an arrival limit sized to the drain rate plus a per-tenant depth ceiling, denials render as a wait estimate, and an admin panel shows depth and oldest-pending age before users feel it.

queue depth nobody is watching

Workpooladvanced55 min

Dead-letter the jobs that ran out of retries, then replay them

Retries end; the work still matters. Jobs that exhaust their retry budget land in a dead-letter table carrying everything needed to run them again days later, an operator replays a single job or a whole outage window from the admin screen, and replaying work that actually succeeded produces no duplicate side effect.

replay that cannot double-fire

Workpoolintermediate45 min

Cancel in-flight work and keep fast jobs out of the slow lane

A five-second thumbnail should not wait behind a ten-thousand-row export. Interactive and bulk work run in separate lanes with independent caps so a burst in one class never delays the other, users cancel their own queued jobs from the UI, and deleting a tenant cancels everything it still had queued.

orphaned jobs after a tenant is deleted

Workpoolintermediate35 min

Size an email pool to your provider's rate limit

A 500-member broadcast should not become 500 simultaneous calls and a wall of 429s. A dedicated pool sized to Resend's documented rate turns that burst into an orderly drain, transient provider errors retry with backoff while bad addresses fail fast, and every send records its final outcome.

sizing a pool to a provider's limit

Workpooladvanced45 min

Stop one tenant's backfill starving everyone else's queue

Queues drain in enqueue order, so one enterprise account's forty-thousand-row reindex parks ahead of everyone else's three-second jobs. Round-robin dispatch with a per-tenant in-flight cap keeps every account moving — a tenant queueing forty thousand rows delays a neighbour by at most one dispatch cycle — and a stalled queue recovers on its own.

fairness needs dispatch, not a bigger cap

Workpoolintermediate40 min

Process uploads through a chained pool without blocking the upload

A saved file is not a finished file. Thumbnails, text extraction and search indexing run as chained stages after an instant upload, each stage retries on its own and reports its own status in the file list, and a failed stage leaves the file usable with its partial results intact.

a failed stage must not lose the file

Workpooladvanced40 min

Keep retries from charging a customer twice

Pool retries are at-least-once, so a job that times out after the provider already accepted it will run again. Every external effect claims a stable key before calling out, providers that accept idempotency keys receive one, and the ambiguous timeout case is reconciled instead of retried into a second charge.

the ambiguous timeout after a charge

Workpoolintermediate30 min

Pause a pool during an incident and drain it before a deploy

When a downstream provider is on fire, stop consuming without dropping work. An operator switch halts admission while enqueues keep succeeding, in-flight jobs finish and the drain reads out live on the admin screen, resuming runs the backlog in its original order, and the pause records who set it and why.

halting admission while enqueues keep working

Collaborative editorintermediate45 min

Add a collaborative editor (Stack walkthrough)

Multiplayer rich text on Convex's sync component with a Tiptap editor: two people type in one document at once and nobody's paragraph disappears. Documents stay scoped to their organization with read and write checks on every sync call, viewers open read-only, and body text becomes searchable once edits settle.

concurrent edits without last-write-wins

Collaborative editorintermediate45 min

SaaS extras: search, plans, webhooks, extensions

Documents become a sellable feature. Plans can gate or cap document creation, search finds body text within one organization only, documents link optionally to projects, editor extensions stay consistent between client and server, and a webhook fires when a draft settles — ready to mirror into a CRM.

search that matches formatted text

Organization overviewintermediate45 min

Build the post-login home dashboard

The first screen after sign-in: who the viewer is, which organization is active, and that organization's projects, updating live. Loading, signed-out and empty states are all handled, switching organizations swaps the whole view without a page reload, and no other tenant's data can ever appear.

loading and empty states per tenant

Organization overviewintermediate45 min

Add a first-run activation checklist to the home dashboard

New teams stall when nothing tells them what to do next. A checklist on the home dashboard tracks real milestones — organization created, teammate invited, first project — from live counts rather than client guesses, hides itself once everything is done, and stays dismissed across devices because dismissal belongs to the organization.

dismissal that survives a new device

Organization overviewintermediate45 min

Add a plan & usage summary widget to the home dashboard

Usage you already meter becomes progress bars on the home screen: one bar per metered capability with a plan ceiling, turning amber at eighty percent and red past the limit. The upgrade link appears only when a tenant is actually close, and raised limits from billing are respected automatically.

warning before the limit, not after

Organization overviewintermediate45 min

Add an org-scoped recent activity feed to the home dashboard

Members see what just happened in their organization: the most recent audit events as a live feed with human-readable labels and relative timestamps. Access is checked against actual membership, so a member of one organization can never read another tenant's history, and unmapped events still appear rather than vanishing.

activity a member is allowed to see

Organization overviewintermediate45 min

Add quick actions and a notifications tile to the home dashboard

Shortcuts for the two things people do most — starting a project and inviting a teammate by email — sit beside a notifications tile with a live unread count, a five-item preview and mark-all-read. Everything reuses existing project, Better Auth and notification functions, so no new backend surface gets invented.

unread counts that stay live

Presencebeginner35 min

Ship org presence with heartbeats

Who is online right now, per organization, from a lightweight heartbeat each member's browser sends. Status moves between online, away and offline from last-seen times, closing a tab clears the entry within the threshold, and two browsers on the same organization see each other appear and disappear live.

leaving cleanly when a tab closes

Presenceintermediate70 min

Scope presence to a record and show the avatar stack

A project page should show who is on that project, not who is somewhere in the workspace. This scopes presence to individual records, so opening a page shows an avatar stack of the people viewing that exact project, with an overflow count past four faces, and each room checks access before revealing anyone.

overflow count past four faces

Presenceadvanced90 min

Live cursors that glide instead of teleporting

Live cursors for shared canvases and boards. Each collaborator gets a labelled pointer in a colour that stays stable across reloads, motion reads as continuous rather than teleporting, positions land correctly across different window sizes and zoom levels, and a cursor that goes quiet fades out instead of freezing on screen.

smooth motion at a low write rate

Presenceintermediate75 min

Typing indicators and advisory field locks

See who is editing which field before you overwrite them. Typing indicators appear on the exact field within a second, a warning shows when two people enter the same field, and locks are advisory — they never block input, and they release themselves when a tab crashes or a laptop lid closes mid-edit.

locks that expire on a closed laptop

Presenceadvanced85 min

Collaborator awareness inside the document editor

Inside the document editor, each collaborator gets a name badge and a coloured highlight over their current selection, and both vanish when their tab closes. Awareness shares the same permission boundary as the document itself, and a presence problem can never touch the content, the undo history, or the sync stream.

one writer for document content

Presenceintermediate70 min

One person, three tabs, one dot

One person with three tabs open should be one dot. Presence collapses to a single entry per user, away means five quiet minutes without keyboard or pointer activity rather than a tab switch, hidden tabs stop reporting entirely, and the server decides the final status instead of trusting whatever a client claims.

three tabs, one presence entry

Presenceintermediate75 min

Ghosts, dropped connections, and the laptop lid

Closed lids, killed tabs and dropped wifi are why presence panels lie. This covers all three: a member whose laptop closes disappears within the away window and their stale row is eventually cleaned up, a brief connection loss shows an honest reconnecting state, and rejoining lands on the same entry with no duplicate.

rejoining without a duplicate row

Presenceadvanced80 min

Keep presence cheap at fifty people in a room

Fifty people in one room stays cheap. This puts a hard write budget on every member, keeps fast-changing cursor data away from the roster everyone reads, and re-renders the member list only when someone actually joins or leaves — so the panel shows a bounded list with a count, not fifty churning rows.

re-render only when the roster changes

Presenceintermediate70 min

Stop presence from leaking what people are working on

Presence should never reveal a private project's name to someone who cannot open it. Location details are redacted per viewer on the server, so restricted records show as online with no location, and an appear-offline setting removes a person from every list — enforced where the data is read, not in the interface.

record names hidden from users without access

Presenceadvanced95 min

Move to @convex-dev/presence without a blackout

Hand heartbeats and disconnect detection to the official Convex presence component while your tenancy checks stay exactly where they are. Old and new run side by side behind a flag, rooms move over gradually with real traffic confirming they agree, rollback is one switch, and nobody ever sees an empty panel.

no empty panel during the cutover

Projects & tasksintermediate45 min

Ship tenant-safe project CRUD

Organization-scoped projects with list, create and archive, each behind permission checks, plan limits and rate limits. Read-only members can browse but never write, limit checks run before anything is inserted, and a guessed id from another organization resolves to nothing — the record's own tenant is what gets authorized, not a value the client sent.

plan limits enforced before the write

Projects & tasksintermediate45 min

Add a multi-tenant task board

A task board with status columns under each project. Tasks take their tenancy from the parent project rather than from anything the client sends, so drag-and-drop status changes stay inside one organization, viewers can follow the board without being able to move a card, and writes are permission-gated throughout.

child records inheriting parent tenancy

Notificationsintermediate30 min

Notify assignees when a task lands on them

Assign a task and the person it lands on knows. Their bell badge updates in every open tab, the notification respects their mutes and skips duplicates fired within the hour, the item joins their next email digest automatically, and the assignment itself is recorded in the audit trail.

respecting mutes before the write

Notificationsbeginner25 min

Give users a notification preferences screen

A settings screen where users control what reaches them: per-type mute switches grouped by category, a master in-app toggle, and a digest frequency picker, each change saved as it is made. Types every product must deliver — billing, security — stay on even if someone tries to mute them by hand.

new notification types defaulting sanely

Notificationsintermediate35 min

Broadcast an announcement to a whole organization

Maintenance windows and policy changes reach every member of an organization, however large. The announcement is delivered in small batches so no single write blows its budget, individual preferences still apply, a retry can never send anyone the same message twice, and admins see how many people got it versus muted it.

no double-sends when a retry lands

Notificationsintermediate35 min

Pop a toast — and a browser push — the moment a notification lands

A badge only helps someone watching the tab. This adds in-app toasts the moment a notification lands — without replaying old ones after a refresh — plus OS-level browser push for people who opt in, tracked per device so revoking one browser leaves the others working. Muted or duplicate notifications never push.

push permission without nagging users

Notificationsbeginner20 min

Expire read notifications before the table swallows your database

Notification history stays proportional to active usage rather than uptime. A nightly sweep clears read items past a retention window you set in one place, works in bounded batches that can never blow a transaction budget, keeps going until the backlog is gone, and logs what it removed so growth stays observable.

bounded deletes that never time out

Notificationsintermediate30 min

Build a full notification inbox with filters and pagination

Past the bell popover sits a full inbox: scrollable history in server-fed pages, filters by type, an unread-only view, and bulk mark-as-read that clears exactly what the current filter shows — clearing billing never silently clears security. The bell links straight into it, so the popover's cap has an escape hatch.

filtering on the server, not the page

Notificationsintermediate30 min

Email urgent notifications immediately instead of waiting for the digest

A failed payment or a sign-in from a new device should not wait for tomorrow's digest. Each notification type carries an urgency level: urgent ones email immediately through the same Resend delivery path the digest uses, mutes still apply, and the digest knows to skip whatever already went out.

digest skipping what already mailed

Notificationsadvanced45 min

Deliver notifications to Slack and customer webhooks

B2B buyers ask for this in week one. Organizations register a Slack channel or their own HTTPS endpoint, choose which event types get forwarded, and every payload arrives signed so the receiver can verify it. Failed deliveries retry, and admins get a per-attempt delivery log plus a test-send button.

signature verification and retry storms

Notificationsintermediate30 min

Brand the digest email and preview it without waiting for the cron

The digest arrives looking like your product: organization branding, items grouped by category with counts, and a one-click unsubscribe that works without signing in and can only ever mute digests. A development-only preview page renders the template with sample data on every save, so iterating never means waiting for the cron.

unsubscribe links that need no login

Notificationsintermediate25 min

Test notification delivery end to end with convex-test

Notification bugs are quiet ones: a muted type that still writes, a digest carrying another tenant's rows. This suite runs the real notification and digest functions against an in-memory backend with no mocks, covering mute suppression, the dedupe window, cross-tenant isolation, unread counts, and digest windows driven by a controlled clock.

faking the clock for digest windows

Transactional emailintermediate45 min

Ship transactional email from debug to Resend

Transactional mail that works from day one. In development, every send renders as a local preview with no provider key required; once production credentials exist, the same path delivers through Resend. The browser only ever learns whether email is configured — the key itself never leaves the server.

keeping provider secrets off the browser

Transactional emailintermediate45 min

Wire auth lifecycle emails (invite + reset)

Organization invites and password resets go out as your own branded mail, wired through Better Auth's lifecycle hooks into the same transactional pipeline production uses. During development they render as local previews, sandbox rules keep test sends away from real inboxes, and the reset flow never reveals whether an address exists.

test sends escaping to real users

Transactional emailintermediate45 min

Close the loop with Resend delivery webhooks

Know what actually landed. Delivery, bounce and complaint signals from Resend flow back into your records, so every message advances through its real lifecycle instead of stopping at a send-time guess, admins read a per-message event timeline, and events that arrive out of order can never drag a status backwards.

webhook events arriving out of order

Transactional emailadvanced60 min

Suppression list and one-click unsubscribe

Addresses that hard-bounce or report spam are suppressed automatically and never mailed again, category opt-outs are honoured, and bulk mail carries the one-click unsubscribe headers Gmail and Yahoo now require, with no sign-in involved. Critical mail like password resets still goes through, and every skipped send stays on record for support.

critical mail must ignore suppression

Transactional emailadvanced55 min

Send exactly once, retry safely, dead-letter the rest

One event, one email, no matter how many times the request fires. A retried call returns the original send instead of mailing twice, transient provider failures back off and retry a bounded number of times, and mail that still will not go parks in a dead-letter state an admin can inspect and requeue.

double sends across retry windows

Transactional emailintermediate50 min

One template source, HTML and plain text, with a preview route

Every template produces the matched HTML and plain-text pair inboxes and spam filters expect, from a single definition that cannot forget the text part. Templates render in the browser with sample data during development, and a test walks the whole registry — including templates added later — and fails if any loses its text alternative.

missing text part hurting spam scores

AI agent & chatadvanced90 min

Ship a workspace assistant with persistent threads

An in-product assistant that answers from the signed-in organization's projects and tasks, streams its reply as it forms, and still holds the conversation after a hard refresh. Its tools are pinned to the caller's organization so the model can never read another tenant, plans can gate access, and the provider key never reaches the browser.

tools that cannot read another tenant

AI agent & chatintermediate40 min

Stream tokens into the reply without one write per token

Assistant replies grow in every open tab as the model writes, at a fraction of the write cost a naive token-by-token stream would spend. A refresh mid-answer resumes where the text left off, awkwardly split network chunks never drop characters, and a straggling chunk cannot reopen a reply that already finished.

streaming backpressure and late chunks

Background agentsintermediate35 min

Add a stale-document report agent

Documents nobody has touched in a month surface in a weekly report. A new agent card appears with its own toggle and Run now button, scheduling, retries, and run history already handled, and runs produce a readable report even before any model key is configured.

per-organization opt-in for scheduled runs

Background agentsadvanced60 min

Human-in-the-loop approval queue

Agents propose, people decide. Every run's output lands in a review queue where a member approves or rejects each suggestion, only approved ones ever write product data, and each decision records who made it and when. Rejections and malformed output leave your records untouched.

rejected output leaving no trace

Background agentsadvanced55 min

Token accounting, cost caps, and model fallback

AI spend stops being a surprise. Every run's tokens count against the plan's daily allowance, an organization past its limit fails fast before the provider is ever called, and organizations nearing the cap finish the day on a cheaper model. Spend and current tier show on the usage and agents screens.

refusing the call before spending money

Background agentsadvanced45 min

Structured output with schema validation and a repair retry

Model replies arrive as typed, validated data instead of prose you parse by hand. A reply with the wrong shape gets exactly one repair attempt, and a second miss marks the run failed with a readable reason — nothing malformed is ever stored or acted on.

one repair pass, then fail cleanly

Background agentsadvanced50 min

Event-triggered agents with debounce and dedupe

Agents react to what happens in the product, not just the clock. A burst of fifty writes starts one run after a short settling window, repeat events inside that window coalesce instead of queueing, a cooldown stops back-to-back runs, and the existing scheduled sweeps keep working untouched.

debounce windows and duplicate triggers

Global searchintermediate35 min

Make a new table searchable in the palette

New tables join the ⌘K palette as first-class results under their own heading, worked through end to end on an invoices table. Isolation lives inside the index itself, so one tenant's records never surface in another organization's palette, and the palette component needs no changes at all.

per-tenant isolation inside a shared index

Analyticsintermediate45 min

Capture tenant-safe product analytics

Product analytics that never ship raw personal data or secrets. Events are typed, redacted before they leave the app, and blocked when a user declines consent, while operators keep a readable local event stream for support. PostHog is optional — everything works in debug mode without it.

redaction before events leave the app

Analyticsintermediate45 min

Maintain a typed event catalog

Event names drift the moment two people add tracking. A shared typed catalog gives every event one agreed name, makes unknown names fail fast in development, keeps redaction on every payload, and leaves product and marketing a documented list of what fires when and with which properties.

undeclared events landing in production

Just like SQLintermediate45 min

Map a SQL list into Convex streams

Bring your SQL instincts to Convex. A status UNION or a parent-child join becomes a live query that stays paginated and tenant-safe instead of loading the whole set to sort in memory, and you finish knowing which SQL clause each piece of the query replaces.

pagination without collect-then-sort

Merging streamsintermediate45 min

Compose stream operators safely

Feeds that read from several ranges at once, merged into one correctly ordered, paginated stream. Covers the pitfalls that quietly break cursors under real load — mismatched merge order, filters doing access control, and page boundaries drifting as data changes underneath — so the feed holds up in production.

cursor drift across merged ranges

Private filesbeginner35 min

Attach private files with Convex File Storage

Private project attachments on Convex File Storage, with no public bucket and no second vendor to set up. Every upload and download is permission-checked, download links expire on their own, and one organization can never list or fetch another's files. Members attach files to projects they can already edit.

expiring links, cross-tenant read isolation

Private filesadvanced75 min

Store large objects in Cloudflare R2

When objects get large or need CDN reach, Cloudflare R2 takes the bytes while Convex still owns every permission check and attachment record. Uploads and downloads run through short-lived links, object keys are scoped per tenant, credentials never reach the browser, and the bucket stays fully private.

tenant-scoped keys on an S3-compatible API

Private filesadvanced70 min

Store large objects in Amazon S3

For teams already on AWS, or with residency rules that name a region, this puts large objects in a private Amazon S3 bucket that still answers to Convex for every permission check and attachment record. Public access stays blocked, links are short-lived, and credentials never leave the server.

regional signing with public access blocked

Private filesintermediate45 min

Delete attachments properly (row and blob together)

Deleting a file should leave nothing behind. You get a permissioned remove action with a confirm step in the UI that clears the attachment record and the stored bytes in one transaction, so nothing lingers as an unreachable orphan and your storage bill matches what users can see.

orphaned blobs after a delete

Private filesintermediate45 min

Enforce the plan's attachment limit at upload time

Your billing plan promises a file allowance; this makes storage honour it. An organization at its limit gets a clear, named rejection before the upload starts — no bytes land, nothing is orphaned — and the person uploading sees an upgrade prompt instead of a raw error. Existing files stay untouched.

counting quota before the upload begins

Private filesintermediate45 min

Validate file type and size before bytes hit storage

One shared allowlist of file types and a size cap, enforced in the browser, again on the server before an upload begins, and once more at save time. Anything that slips past the first two checks is removed immediately, so a rejected upload never leaves stray bytes behind.

clients that skip the browser check

Private filesintermediate45 min

Add an organization logo upload

The team-logo setting, done properly. Each organization holds exactly one current image, the settings page preview updates live the moment a new logo lands, and every replaced image is deleted on the spot — so changing the logo five times never leaves four dead files in storage.

cleaning up the image you replaced

TanStack Formsintermediate45 min

Ship a TanStack Form create flow for products

A TanStack Form create flow where the form and the backend agree on what counts as valid. Field and submit checks surface errors inline as people type, the server independently refuses anything invalid, and an accepted row clears the form and appears in the product list immediately.

validation the client cannot skip

TanStack Tablesintermediate45 min

Ship a Convex-backed TanStack product grid

A sortable, searchable, filterable product grid backed by live Convex data, with pagination and a seed action for demo rows. Every read is scoped on the server, so members only ever see their own organization's records, and new or edited rows appear in the grid without a refresh.

server-side scoping behind client-side sorting

Architecture guidebeginner45 min

Apply the architecture runbook

A new engineer's first week, compressed. You finish able to say where Better Auth ends and your Convex product data begins — who owns identity, sessions, organizations, and authorization decisions — with each section of the guide mapped to the real code that implements it, verified by the standard checks.

the boundary between auth and product data

Component labsintermediate45 min

Use Labs as your integration smoke harness

One admin screen proves that email, analytics, error reporting, durable workflows, and plan gating really work on a fresh clone before a demo or a deploy. You finish able to read each card's Debug versus Configured badge and name exactly what flips an adapter live — no vendor dashboards required.

telling debug mode from real configuration

CRMintermediate50 min

Make an edit to a row fire an automation

Let a change to an existing record start an automation, not just a brand new one. The updated trigger appears in the automation builder for your own tables, an edit never re-runs rules somebody wrote for created records, and a save that changes nothing stays silent — proven in both directions by tests.

edits and inserts stay separate triggers

Helpdeskintermediate45 min

Attach a durable first-response deadline to any table

Give a record in any table a first-response deadline that outlives your next deploy. Nothing polls for overdue work, the wake-up at the deadline records exactly one breach, one notification, and one audit event even if it runs twice, and answered or closed records never escalate at all.

a timer that survives a deploy

Integrations guidebeginner45 min

Apply the integrations runbook

Optional providers such as Resend, PostHog, and Sentry run in debug mode from day one, so every feature works before any account exists. You finish knowing exactly which setting flips each provider live, with the Labs badges confirming what is configured — no guessing about which mode you are in.

knowing which adapter is really live

Migrationsintermediate60 min

Finish the tenancy cutover and make organizationId required

Move a running database onto its new tenancy column with nobody signed out. You dry-run first, backfill in batches with live progress you can watch and resume, count remaining legacy rows down to zero, then tighten the schema so the platform itself refuses the old shape forever.

a cutover with nobody signed out

Platform adminintermediate45 min

Protect admin writes with password step-up

Operators keep open access to admin reads, but anything destructive asks for the password again, and that approval lasts only fifteen minutes. Better Auth verifies the password, a stale window produces a clear re-auth error, and every sensitive action lands in the audit trail with who did it.

a stolen session that is still signed in

Platform adminintermediate45 min

Operate global feature flags safely

Turn a feature off for everyone during an incident, ramp it to a percentage of organizations with stable canary membership, or override a single customer. Everything happens from the admin console with no redeploy and no code change, gated behind password step-up and recorded in the audit trail.

per-org overrides on top of a rollout

saaszero CLIintermediate45 min

Make your own module installable

Modules you write become installable and removable like the ones that ship in the box. You register the module with the CLI, and a drift check keeps its published copy in step with your source, while a remove-and-reinstall round trip proves an uninstall leaves the codebase byte-identical to where it started.

byte-identical removal from shared files