DEV Community

lizer yang for SmartGate

Posted on

AI Automation for SaaS Operations: Queues, Webhooks, Billing

Originally published at AI Automation for SaaS Operations: Queues, Webhooks, Billing on smartgate.network.

A shorter version of "AI Automation for SaaS Operations: Queues, Webhooks, Billing" — the full piece lives at smartgate.network.

What the full piece covers

  • The short version for whoever signs off on the automation roadmap — Two unrelated things are sold under one label.
  • What the numbers say about AI workflow automation — ai workflow automation shows 1,000 US searches a month; workflow automation shows 2,900.
  • enqueueEmail: the write side of the queue is two lines long — async function enqueueEmail(data: EmailJobData) { await enqueue("email", { type: data.type, payload: data as unknown as Record, }); } The producer knows nothing: no template rendering, no provider call, no retry.
  • processNextEmail: one job per call, and why the loop lives elsewhere — async function processNextEmail(): Promise { const job = await dequeue("email"); if (!job) return false; switch (job.type) { case "welcome": { const { to, firstName } = job.payload as { to: string; firstName?: string }; await sendWelcomeEmail({ to, firstName }); break …
  • startEmailWorker: a polling loop with a predictable ceiling — async function startEmailWorker(pollIntervalMs = 5000) { // eslint-disable-next-line no-constant-condition while (true) { try { await processNextEmail(); } catch (err) { console.error("[email-worker] Error processing job:", err); } await new Promise((resolve) => setTimeout(resolve, pollIntervalMs)); } } Read the loop as a capacity …
  • handleProviderWebhook: verify, dedupe, then dispatch — async function handleProviderWebhook( providerId: string, request: Request, ): Promise { const provider = getProvider(providerId); if (!provider) { return new Response(Unknown webhook provider: ${providerId}, { status: 404 }); } const rawBody = await request.text(); if (!(await provider.verify(rawBody, request.headers))) { if (process.env.NODE_ENV …
  • processNormalizedEvent: one switch, three subscription states — async function processNormalizedEvent(event: NormalizedWebhookEvent): Promise { if (!event.teamId) { if (process.env.NODE_ENV === "development") { console.warn( "[webhook] skipped plan update: missing teamId", event.type, event.externalCustomerId, ); } return; } const mapped = resolvePlanFromPriceId(event.planProductId ??
  • parsePaddleEventId: the third source of an idempotency key — function parsePaddleEventId(rawBody: string): string | null { try { const parsed = JSON.parse(rawBody) as { event_id?: string }; return parsed.event_id ??
  • runQuotaEmailNotifications: scan teams, decide per member — async function runQuotaEmailNotifications(): Promise { const result: QuotaEmailJobResult = { teamsScanned: 0, emailsSent: 0, skipped: 0, errors: [], }; if (!process.env.RESEND_API_KEY || !process.env.EMAIL_FROM) { result.errors.push("RESEND_API_KEY or EMAIL_FROM not configured"); return result; } if (!isRedisConfigured()) { result.errors.push("Redis required for usage lookup …
  • …plus 8 more section(s).

Read the full piece: AI Automation for SaaS Operations: Queues, Webhooks, Billing on smartgate.network.

Top comments (0)