🛡️ Real production starts here
In modules 6.1 and 6.2, you put the code in the cloud (GitHub + Trigger.dev) and secured it (env vars, RLS, auditing). What’s missing is what separates a polished prototype from a product that can handle real life: when something fails on its own, you find out and have a way to fix it — and, as a bonus, it generates revenue. It’s the sum of reliability (topics 1 through 6) and monetization (topic 7).
Before the seven topics, take a look at what changes. In an interactive session, the AI reads the error and fixes it right away—a self-healing loop. But the task hosted runs without anyone watching: it executes, but doesn’t adapt. You recreate this loop with platform features (logs, retries, alerts) and better prompts; then close the revenue loop with Stripe.
Illustrative diagram — reliability recreates, with a platform and prompts, the healing loop you got for free in the interactive session; monetization closes the cycle by turning the workflow into a product.
🕳️ The agentic gap revisited
What it is
During development, you and the Claude Code work in a self-healing loop: the task runs, errors, and the AI reads the error right away and fixes it. But when the task is deployed and starts running on its own — on a schedule (cron) or triggered by an event (webhook) in the cloud — this loop disappears. The agentic gap is exactly this gap: the hosted task runs, but it doesn't adapt, and no one is reading the errors.
What is it?Agentic gap — the difference between the interactive session (AI is watching and correcting) and autonomous execution in the cloud (no one is watching). Silent failure — when a task breaks but no one is notified: it just “stops working,” and you find out later.
In practical terms: you close the terminal, go to sleep, and at 7 a.m. cron triggers the task. If a source's API is down, the task can freeze and die — and since Claude Code is no longer monitoring, the failure goes unnoticed until someone complains. The solution isn’t to “hope for the best”: you close the gap with platform resources (retries, traces, alerts) and with better prompting (error handling and logging requested from the very first prompt).
Why learn
Without closing the gap, “it ran in the demo” doesn’t mean “it runs in production.” An automation that fails silently is worse than none: you believe it’s taking care of something that stopped working days ago. Understanding the gap changes how you build—you start asking for robustness before of deployment, not after the incident.
🎯 Practical tip
Before deploying any task, answer two questions: "What happens if API X goes down?" and "How will I know if this fails in the middle of the night?" If you can't answer both, it's not ready for production yet.
Key concepts
AI no longer reads the errors.
Runs, but doesn’t fix itself.
Retries, traces, alerts.
Error and log from the start.
📝 Meaningful Logging
What it is
O Trigger.dev gives you visibility (traces) and automatic retries, but it can only retry what recognizes how it fails — and you can only diagnose what the log tells you. Meaningful logging means asking AI for a clear log entry at each important step, with specific context: not “task failed,” but “Firecrawl returned 429 on source 3.” A vague log is useless; a specific log is actionable.
What is it?Log — a line of text the task writes while it runs, recording what happened. 429 — HTTP code for “too many requests” (rate limit). A log that cites the code and which source failed tells what went wrong and where.
Illustrative diagram — the same failure, two logs. The specific one carries enough context for you (or the AI) to act without guessing.
Why learn
When something fails at 3 a.m., the log is all you'll have. A trace full of generic "ok" and "error" messages forces you to reproduce the problem from scratch; a trace with rich messages tells you in seconds which source went down, with which code, at which step. Asking for good logs from the very first prompt is cheaper than adding them after the incident.
🎯 Goal: ask Claude Code for clear, specific logs at each significant step of the task.
Revise os arquivos da task <nome-da-task> e adicione um log claro em cada passo significativo. Em vez de mensagens genéricas como "task failed", logue o contexto: serviço, código de status e qual item. Exemplos do que eu quero ver nos logs: - "Firecrawl retornou 429 na fonte 3 (rate limit)" - "Supabase: gravada linha parcial para lead <id> (1 fonte faltando)" - "OpenAI ok: resumo gerado para <empresa> em 1.4s" Use o logger do Trigger.dev (logger.info / logger.warn / logger.error) para que apareçam no trace do dashboard.
✅ How to verify: run the task in dev, open the trace in the Trigger.dev dashboard, and check that each step has a readable line with context — not just “ok”/“failed”.
Key concepts
Service, code, item.
Not just at the end.
You can act without guessing.
Visible on the dashboard.
🧯 try/catch in external calls
What it is
Every call to an external API can fail — the network goes down, the service returns an error, or the response comes in an unexpected format. The try/catch is the pattern that wraps this call so that, if it fails, the task capture the error instead of letting it die: you log the error with context, continue where possible, and only launches (stops permanently) on truly critical failures. That's what allows the task to write a partial row instead of breaking when a source goes down.
What is it?try/catch — a code structure: the block try tries something risky; if it errors, the block catch takes over instead of letting the task get stuck. Partial line — save what worked (3 of 4 sources), noting what was missing instead of discarding everything.
✓ With try/catch (fails gracefully)
- ✓Source 3 goes down → logs "429 from source 3" and continue.
- ✓Writes the row with 3 sources, marking the 4th as missing.
- ✓Only stop if something is missing critical (e.g., the database is down).
✗ No try/catch (everything gets stuck)
- ✗Source 3 goes down → the exception goes up and kills the entire task.
- ✗You also lose the sources that had worked.
- ✗Silent failure: nothing is saved, and the error has no context.
Why learn
There's a real case that proves the point: the Firecrawl returned success:false with the results under the key web instead of data. Without error handling, this would have broken the read and crashed the task. With the try/catch in place, the task detected the unexpected format, logged the issue, and didn’t freeze — saved what it could. Error handling isn’t a luxury: it’s what keeps the automation running when the outside world misbehaves.
🎯 Goal: fail gracefully — wrap the external call in try/catch, log with context, and continue in noncritical cases.
// TypeScript — chamada a uma API externa dentro da task
let dadosFonte = null;
try {
const resp = await fetch(`https://api.<servico>.com/<rota>`, {
headers: { Authorization: `Bearer ${process.env.<CHAVE>}` },
});
if (!resp.ok) {
// falha não-crítica: loga COM contexto e continua
logger.warn(`<Servico> retornou ${resp.status} na fonte ${fonteId}`);
} else {
dadosFonte = await resp.json();
}
} catch (err) {
// erro de rede/timeout: registra e segue sem essa fonte
logger.error(`Falha ao chamar <Servico> (fonte ${fonteId}): ${err.message}`);
}
// grava a linha mesmo parcial — não descarta o que deu certo
await salvarLinha({ ...resto, fonte: dadosFonte ?? "faltando" });
// só lança se faltar algo CRÍTICO (sem isso não há resultado)
if (!dadoEssencial) throw new Error("Dado essencial ausente — abortando");
✅ How to verify: simulate the failure (wrong key / source unavailable) and confirm in the trace that the task doesn’t freeze — it logs the warning and saves the partial line. Only a truly critical failure should stop the run.
Key concepts
catch prevents the task from dying.
Log the service and code.
Writes a partial row.
throw only for the essentials.
🔁 Retries with backoff (vs. logic bugs)
What it is
You configure retries in the task definition itself (e.g., up to 3 attempts, with exponential backoff starting in 30s). If the task fails for a temporary reason, Trigger.dev waits and tries again, doubling the interval each time. The golden rule: retries solve transient failures (timeout, rate limit, network instability) — but no fix logic bugs in your code.
What is it?Retry — automatically try again after a failure. Exponential backoff — wait longer between each attempt (30s, then 60s, then 120s), giving the service time to recover and avoiding hammering it. Transient failure — a temporary error that tends to go away on its own.
Illustrative diagram — if all 3 attempts fail the same way, the problem isn’t the timing: it’s the logic. Retrying a bug forever just burns resources.
Why learn
Knowing the difference helps you avoid two opposite mistakes: not have retries (a minor hiccup brings down a run that would have worked 30s later) and trust them too much (a logic bug gets retried forever, masking the real problem and wasting executions). If all three attempts fail with the same error, stop retrying and start debugging — the subject of topic 6.
🎯 Goal: tolerate transient failures by configuring retries with exponential backoff on the task.
// trigger/<sua-task>.ts — configuração de retries
export const minhaTask = task({
id: "<id-da-task>",
retry: {
maxAttempts: 3, // até 3 tentativas
minTimeoutInMs: 30_000, // backoff começa em 30s
factor: 2, // exponencial: 30s → 60s → 120s
},
run: async (payload) => {
// ... sua lógica aqui ...
},
});
✅ How to verify: Trigger a transient failure (rate limit) and check the trace to see Trigger.dev reschedule the attempt after ~30s and continue. If the SAME error repeats across all 3 attempts, it’s a logic bug — more retries won’t help; go debug it.
Key concepts
Number of attempts (e.g., 3).
Interval increases (30s, 60s…).
Retry is for the passenger.
Same error 3 times = debug it.
🔔 Alerts (email/Slack/webhook)
What it is
Logs and traces tell you what happened — if you’re going to look. Alerts turn that around: they come to you. In Trigger.dev, at Project Settings > Alerts, you configure notifications by email, Slack or webhook triggered by deploy or run failures, with configurable environment and failure type. It takes less than two minutes to set up.
What is it?Alert — an automatic notification when something goes wrong. Webhook — an address (URL) that receives a notification from the platform and can trigger another automation. An alert is the opposite of a log: instead of you going to the information, the information comes to you.
✓ With alerts
- ✓The overnight run fails → you get an email/Slack notification right away.
- ✓You discover before for the end user to complain.
- ✓The webhook can even open a ticket or notify a team channel.
✗ No alerts
- ✗The task breaks and sits idle for days without anyone noticing.
- ✗You only find out when a customer tells you it “stopped working.”
- ✗Trusts "going to check the dashboard"—and never checks at the right time.
Why learn
This is the rule that closes the agentic gap in practice: every deployed task needs a failure alert. Without this, you’re betting that you’ll check the dashboard exactly when something goes wrong—which never happens. With alerts, the system contacts you; you replace "hope" with "a warning." Since it’s configurable by environment, keep production alerts on at all times.
🎯 Practical tip
Add “configure failure alert” to your deployment checklist, alongside “check env vars.” Go to Project Settings > Alerts, choose the channel (Slack is usually the most visible for teams), mark the environment prod and the failure types (run failed, deploy failed). Two minutes now can save days of dead tasks later.
Key concepts
Email, Slack, webhook.
Run failed, deploy failed.
Production always on.
The alert comes to you.
🧑🔧 The human debugging loop
What it is
Claude still debugs — it just doesn’t automatically and more. In production, the healing loop exists, but with you in the middle: the task fails, the alert arrives, and you (the human) bridge the gap. Open the red trace in the dashboard, copy the error, paste it into Claude (“here’s the trace that failed: <error>, what’s wrong and how do I fix it?”), let Claude identify the fix, make the redeploy and test it. The trace + Claude provide a quick diagnosis; the human just needs to pass along the error.
What is it?Trace — the step-by-step record of a run in the dashboard; a red/failed trace shows where execution broke. Redeploy — upload the corrected version of the code to the cloud again (commit → push → deploy) so the fix takes effect in production.
The human debugging loop, step by step
-
1
Open the red trace
In the Trigger.dev dashboard, find the failed run (status failed) and open the trace to see which step broke.
-
2
Copy the error
Select the complete error message (including the stack/context your meaningful log captured).
-
3
Paste it into Claude and ask for a diagnosis
"Here's the trace that failed: <paste the error>. What's wrong, and how do I fix it?" — Claude reads the context and suggests a fix.
-
4
Apply the fix
Let Claude edit the code to apply the identified fix; review the change (trust but verify).
-
5
Redeploy
commit → push → deploy: the corrected version goes live in the cloud.
-
6
Test
Trigger the task again (or wait for the next cron) and confirm in the trace that it now passes in green.
Illustrative timeline—the loop is the same as in the interactive session (error → diagnosis → fix → redeploy → test), but now you is what carries the error to Claude.
Why learn
This is where everything connects: the meaningful log (topic 2) makes the trace readable, while the alerts (topic 5) tell you to open that trace, and the retries (topic 4) already tell you whether it’s worth debugging (same error 3x = bug). The cycle is fast because the trace + Claude handle the heavy diagnostic work; your part is just the bridge. Mastering this loop is what keeps a hosted automation running for weeks.
🎯 Goal: take the error from a failed trace and ask Claude to diagnose and fix it.
Aqui está o trace que falhou: <cole o erro completo copiado do dashboard do Trigger.dev> Contexto: é a task <nome-da-task>, rodou via <cron/webhook> em produção. O que está errado e como conserto? Aplique o fix nos arquivos da task e me explique em uma frase a causa raiz.
✅ How to verify: apply the proposed fix, redeploy (commit → push → deploy), and retest the task—confirm in the trace that it passes in green. If it fails again with the same error, paste the new trace and repeat.
Key concepts
Where the run failed.
You bridge the error.
Reads the context, proposes the fix.
Close the loop with a green status.
💳 Monetization with Stripe
What it is
The step that turns the automation into a SaaS: add payments with Stripe in a model freemium — a limited free tier (e.g., 2 lead qualifications per day) and a paid pro tier (e.g., US$ 29/month, unlimited), all through Stripe Checkout. The motivation isn’t just making money: it’s limit usage so you don’t end up paying for users’ uncapped AI usage. That’s what completes the transition from “just a workflow” to “an app that generates revenue.”
What is it?SaaS — software sold as a subscription. Freemium — free tier with limits + paid tier without limits. Stripe Checkout — Stripe's ready-made payment page. Webhook — an endpoint Stripe calls to notify your app that a payment occurred.
The good news: you don’t write the integration by hand. You describe the result, and the Claude Code implements — but you you need to create the accounts, copy the four keys, and run the migration. The sequence below is the manual part; Claude handles the code.
Stripe setup sequence
-
1
Create the recurring product
In Stripe, create a recurring monthly product (e.g., Pro, US$ 29/month) and copy the price ID (starts with
price_…). -
2
Get the API keys
In Developers → API keys, copy the publishable key (
pk_…) e a secret key (sk_…). -
3
Create the webhook endpoint
Point to
.../api/stripe-webhooklisteningcheckout.session.completed,customer.subscription.updatedecustomer.subscription.deleted; copy the webhook secret (whsec_…). -
4
Run the migration in Supabase
Run the migration SQL to create the table
subscriptionsin Supabase (stores who subscribed and the status). -
5
Add the 4 keys in Vercel + redeploy
Add the four env vars (publishable, secret, price ID, webhook secret) in Vercel and redeploy for them to take effect in production.
-
6
Test with a test card
Use a Stripe test card (e.g.,
4242 4242 4242 4242) and confirm that the subscription shows as active.
Illustrative timeline—four keys (publishable, secret, price ID, webhook secret), a new table, and a webhook endpoint: this is the framework for any subscription billing.
Why learn
Without a usage limit, each free user can trigger unlimited AI usage—and the bill lands in your lap. Freemium solves both sides: the free tier (2/day) is a showcase and cost safeguard; the pro tier (US$ 29/month unlimited) generates revenue. With that, what was a technical workflow becomes a product that holds up — the final leap in the learning path.
🎯 Goal: ask Claude Code to integrate payments with a free tier and a pro tier via Stripe Checkout.
Adicione pagamentos Stripe ao app com modelo freemium: - Tier grátis: até 2 qualificações de lead por dia. - Tier pro: US$ 29/mês, ilimitado, via Stripe Checkout. Use as env vars: STRIPE_PUBLISHABLE_KEY, STRIPE_SECRET_KEY, STRIPE_PRICE_ID, STRIPE_WEBHOOK_SECRET. Crie a rota /api/stripe-webhook escutando checkout.session.completed, customer.subscription.updated e customer.subscription.deleted, e atualize a tabela subscriptions no Supabase conforme o status. Bloqueie o uso acima do limite no tier grátis.
✅ How to verify: open the pricing page, click Subscribe and complete the checkout with the test card (4242…). Check that the table subscriptions marked the user as pro and removes the 2/day limit for them.
Key concepts
Limited free + paid pro.
pk, sk, price ID, whsec.
Stripe notifies your app.
Workflow → recipe.
Wrapping up reliability: automatic retries solve which type of failure?
📌 Module Summary
Next:
You completed Track 6 — Production, Deployment & Security. From the mindset to a product that generates revenue: the complete Vibe Coding journey is finished.