Skip to content

How we build product workflows

Velyn Dental and BuckHound both rely on the same engineering basics: clear triggers, fast responses, useful records, and fallbacks when a dependency fails.

What we are building: Product workflows that answer calls, watch prices, and prepare follow-up without hiding what happened from the user.

Core Principles

Background Work With Clear Triggers

Some work should happen without a user babysitting the screen: checking prices, handling calls, and preparing follow-up.

Event-driven architecture with scheduled checks, explicit thresholds, and traceable actions.

Models Are Only Part of the Product

The hard parts are reliability, latency, fallbacks, and the rules around when to alert or hand off.

Streaming pipelines, fallback chains, and state machines that handle edge cases gracefully.

Latency Changes Trust

A voice workflow that waits too long feels broken. A price alert that arrives late is not useful.

Parallel processing, warm connections, predictive caching, and aggressive optimization.

Designed for Early Scale

Serverless-first architecture keeps small launch traffic simple and gives busy periods room to grow.

Vercel Functions + edge compute, connection pooling, and usage-based pricing that scales linearly.

Fail Gracefully, Always

APIs time out. Networks drop. Models can fail. The product needs a clear fallback instead of a silent dead end.

Circuit breakers, fallback responses, retry logic with exponential backoff, and monitoring at every layer.

Ship Fast, Learn Faster

Real use teaches what demos cannot. We optimize for small releases, visible monitoring, and fast correction.

Feature flags, canary deployments, real-time monitoring, and weekly release cycles.

Architecture Patterns

These patterns recur across the product portfolio. They keep alerts, call handling, and follow-up work understandable under stress.

1

Event-Driven Monitoring

Problem

How do you know when to take action without constantly asking?

Solution

Continuous background jobs (cron) that poll external state and trigger actions when conditions are met.

Real Example

BuckHound™ checks Amazon, eBay, and Newegg on a recurring loop. When a saved item hits the shopper's target or drops below its usual range, it sends a push notification to the shopper's web or iOS app.

// Vercel cron runs every 4 hours
export async function GET(req: Request) {
  const products = await getActiveProducts();

  for (const product of products) {
    const currentPrice = await fetchPrice(product.asin);

    if (shouldAlert(product, currentPrice)) {
      await sendPushNotification(product.userId, {
        product: product.name,
        newPrice: currentPrice,
        retailer: product.retailer,
        savings: product.lastPrice - currentPrice,
      });
    }
  }

  return Response.json({ checked: products.length });
}
2

Streaming Pipelines

Problem

Voice AI needs <2s latency. Sequential processing takes 5-10s.

Solution

Stream every layer: transcription → LLM → synthesis. Start speaking before the LLM finishes thinking.

Real Example

Velyn Dental streams Google speech-to-text into Claude (via AWS Bedrock), which streams to voice synthesis — targeting sub-2-second latency.

// Stream LLM response to voice synthesis
const stream = anthropic.messages.stream({
  model: "claude-haiku-4-5-20251001",
  messages: conversationHistory,
  max_tokens: 300,
});

for await (const event of stream) {
  if (event.type === "content_block_delta" && event.delta.type === "text_delta") {
    // Synthesize and play immediately
    await synthesizeAndStream(event.delta.text);
  }
}
3

Confidence-Based Alerts

Problem

How do you prevent false positives from over-alerting users?

Solution

Every alert needs enough confidence to be useful. Low confidence means wait, suppress, or ask for review.

Real Example

Before sending a price alert, BuckHound checks recent movement, target price, and user preferences.

interface Decision {
  shouldAct: boolean;
  confidence: number; // 0-100
  reasoning: string;
}

function makeDecision(context: Context): Decision {
  const signals = analyzeSignals(context);
  const confidence = calculateConfidence(signals);

  // Only act if confidence > 80%
  return {
    shouldAct: confidence > 80,
    confidence,
    reasoning: explainDecision(signals),
  };
}
4

State Persistence

Problem

What did the product already tell the user, caller, or team?

Solution

Important interactions persist to the database. Decision engines check history before acting.

Real Example

Velyn Dental can use previous call records from the same number so staff do not start from scratch.

// Check if we already alerted about this
const recentAlert = await db.alert.findFirst({
  where: {
    userId: user.id,
    productId: product.id,
    createdAt: { gte: subHours(new Date(), 24) },
  },
});

// Don't spam - wait 24h between similar alerts
if (recentAlert) {
  return { shouldAlert: false, reason: "Recently alerted" };
}

Key Technical Decisions

Every choice is a trade-off. Here's what we chose and why.

Serverless over containers

AI workloads are spiky. Serverless scales to zero during off-hours and handles 100x spikes instantly. No DevOps overhead.

PostgreSQL over NoSQL

Relational data (users, calls, products) with strong consistency requirements. Prisma provides type-safe queries.

Monorepo over microservices

Small team, shared types/utilities. Microservices add coordination overhead we don't need at this scale.

Telnyx over custom WebRTC

Telephony is complex. Telnyx handles PSTN connectivity, HIPAA-eligible transport under a signed BAA, and reliability. We focus on AI logic.

Managed model APIs over self-hosted models

Inference cost ($0.03/min) is negligible vs engineering time. Self-hosting adds operational complexity without ROI.

Monitoring > Testing

Unit tests catch syntax errors. Production monitoring catches real user problems. We invest heavily in Datadog + Sentry.

What we do differently

The product keeps working between visits

Customers should not have to keep checking the same screen. The product watches the narrow workflow it was built for, then shows a clear record when something needs attention.

  • Keep checking calls, prices, and tasks in the background
  • Open with the details that explain why the alert fired
  • Prepare the follow-up: answer the call, send the alert, create the task
  • Keep the history attached for whoever touches it next

Example: A shopper should not have to ask whether a saved item dropped. BuckHound checks the item in the background and sends an alert when it crosses the target price.

That requires background jobs, state management, alert rules, and push-notification delivery. That is the product work we build.

What's next

Right now the products catch what matters and show the record. The next step is handling more of the follow-through, such as appointment booking and richer alert decisions, while keeping the user in control.

We ship what works today and add more automation only where trust is earned.

Want to see this approach in action?

We use cookies to enhance your browsing experience and analyze site traffic. Accept, reject, or customize below.

Read our Privacy Policy and Terms of Service