Email outbox backend
I'm sorry for whoever reviews this. I tried to split it up as much as possible so this is only the existing functionality migrated onto the new email outbox table; the new endpoints or UI changes are not here yet.
Tests not passing yet but already ready for reviews.
How to review:
- The most important file is schema.prisma. It contains the explanation of the new EmailOutbox table, and all its constraints.
- After that, check the updated emails.tsx. It writes to the EmailOutbox table.
- Then, check email-queue-step.tsx. It contains the logic that renders, queues, and sends an email (alongside emails-low-level.tsx). This is called once every ~second (in a Vercel cron job in prod or a script in dev) and whenever a new email is sent (see the callers of runEmailQueueStep for more info).
- Then, look at the updated endpoints. Would love to have some eyes on the ones that @BilalG1 wrote in particular.
- Finally, everything else. Everything else should be relatively straight-forward, I think, mostly migrations.
The prompt that generated the first version (but note that the final version is somewhat different from what was generated, but it might help get some overview):
i want you to implement a new email sending backend infrastructure. apps/backend is the right folder
let's start with the database. For this purpose, I've already created an EmailOutbox model in the prisma schema. Note that, for now, canHaveDeliveryInfo is always false (we never fetch delivery info rn). Note that the EmailOutbox is on the global database, not the source-of-truth database (which means it's not in the same database as the project users, for example, and can't have foreign key relations to those). You must create a new migration for this new schema; you will have to manually write the migration SQL for it (can't use the Prisma command). After that, create another subsequent migration that migrates from the old SentEmail table to the new one.
Create a file in `lib` that has functions for the email outbox for listing all emails (returning the database objects), editing an email, resetting an email, deleting an email (which should only work under the same conditions as the retry one). This file should be SUPER clean, make sure to have many explained/documented functions with concise docs.
You also want to update emails.tsx to use the new email outbox. The usual current email-sending functions should instead of sending the email directly, create an EmailOutbox row in the database instead. It should also queue the email-queue-step below onto QStash so that it can start queuing itself repeatedly (this should probably be exported as a function so. Keep the existing function implementations somewhere for your own reference until you've implemented the email queue step endpoint down below, as you'll need to implement the email queue step endpoint down below with them. Note that if you send from a templtae to many users, each user is its own row in the email outbox system. Feel free to update the signature of the send email functions to match the information that is required in EmailOutbox, just update all the callers.
Also, update the `email-rendering.tsx` file to export a new function that allows you to render many email templates/drafts/themes at once, as long as they are for the same tenancy ID. They should all be in a single call to Freestyle.
Note that difference between InternalError and ExternalError in the EmailOutbox schema. Internal errors contain all the information, but maybe have sensitive implementaiton-speciifc data that should not exposed to the end user who sent the email. External errors are guaranteed to be safe to expose to the end user who sent the email. There is already a similar handling that's implicit in the emails.tsx file, so take some of its logic and make it more explicit (for both rendering & sending server errors, make functions that take internal full error objects and returns an external error object, with just a string like "Internal server error" if it's an unknown error).
Also, create the following endpoints (please look at other endpoints in the backend to see how they're implemented, and make sure the security is good):
- GET /api/latest/emails/delivery-info: This endpoint returns some stats on the emails. It should return the number of emails sent in the last hour, last day, last week, and last month, and how many of those emails (per hour, day, week, and month) were marked as spam or bounced. It should also return the "capacity rate": the number of emails that can be sent per second. It should be calculated like this: `max(max(<the number of emails sent in the last week> * 4, 200) * penaltyFactor, 5) / 7 / 60 / 60`, where penaltyFactor is a number between 0 and 1 depending on what % of emails were bounced or marked as spam (I will build a precise metric for this later, for now you can think about it and make your own).
- GET /api/latest/emails/outbox: List all emails in the outbox, paginated like the users endpoint. Should return a nice type for the individual rows which should at least contain the status and simple status, and probably some other useful fields that you think should be displayed to the user in the UI, but not all those from the database (if you're not sure whether it's needed, don't include it, we can include it later). The function which converts DB EmailOutbox objects to the API type should be separate from the other stuff as we will use it elsewhere too.
- PATCH /api/latest/emails/outbox/{id}: Edit an email in the outbox, if the conditions for it are met. Only some fields can be edited, see the Prisma schema for an explanation
- POST /api/latest/emails/outbox/{id}/cancel: Cancel an email in the outbox, if the conditions for it are met
- POST /api/latest/emails/outbox/{id}/retry: Retry an email in the outbox, if the conditions for it are met. See the Prisma schema for an explanation
- POST /api/latest/internal/email-queue-step: This is a QStash receiver that is constantly called by QStash in an infinite loop. Make sure that it is super clean and easy to understand. Feel free to split it up into multiple functions and files as needed.
- - First thing it should do is add a call to itself back into the QStash queue, with Flow Control with a flow control key equal to the URL, a rate limit of 1 per second, and a parallelism limit of 1.
- - Then, it should acquire a Postgres exclusive lock, to make sure that only one email-queue-step can be running at a time (make sure this is easy to understand and free of data races). It should be released automatically at the end of this endpoint (think about how we can make it that there's no deadlock if the endpoint fails for whatever reason, eg. the server it's running on dies).
- - Now, it should get the timestamp of the last execution of this endpoint (which is stored in some metadata table in the global database), and calculate the precise time delta in seconds. The new time should be updated in the metadata table. (Make sure that it's not negative, which can happen due to clock skew, so make sure to handle that by just not doing anything if the new time is before the last execution time).
- - Then, it should fetch all the email outbox rows that do not have a renderedByWorkerId, and set renderedByWorkerId to a random UUID and startedRenderingAt to now (it should do this in an atomic statement so that an email is never rendered by multiple workers at the same time). It should then use email-rendering.tsx to render the email, using the previously created batch-by-tenancy function (you need to group the email outbox rows by tenancy ID for this). Once done, it should (again, in a single atomic statement) check whether the renderedByWorkerId is still set to the same UUID (this is to make sure there are no race conditions), and if so, set the appropriate fields in the EmailOutbox row.
- - Next, it should run a SQL query that updates all rows with scheduledAt < now() and isQueued = false to set isQueued = true.
- - Now, it should fetch a deduplicated list of all tenancy IDs of all email outbox rows that have isQueued = true and startedSendingAt being null, and set startedSendingAt to now.
- - For each tenancy ID in the list:
- - - Calculate the capacity rate. Multiply it by the time delta since the last endpoint execution. Use stochastic rounding to round to the nearest integer.
- - - For that tenancy, fetch at most that many email outbox rows that have isQueued = true and startedSendingAt being null, and set startedSendingAt to now. Fetch those rows that have the highest priority (see the Prisma schema for an explanation).
- - - Then, asynchronously in parallel, process each email that needs to be sent. Use the email-sending functions to send the emails, and update finishedSendingAt and canHaveDeliveryInfo (recall it's always set to false for now) for each row when the email is sent. Before you send the email, check whether the user still exists in the DB, and use the same notification category logic from the send-email endpoint (extract it into a helper function) to find out which users have already unsubscribed and should not receive the email, and set skippedReason accordingly if necessary. If the email encountered an error while sending, set the sendServerError-ish fields in the same atomic statement. `waitUntil` each promise (to make sure that they continue running in the background). Ensure that a bug when handling one of the emails does not mess with the other emails. Don't use `.catch`, ever, use try-catch blocks, and use `captureError("<some-machine-readable-location-id>", ...)` to capture all unknown errors aggressively.
While you're at it, we now have some new features like scheduling, add those to the send-email endpoint.
Make sure to make VERY comprehensive E2E tests for ALL the edge cases.
The code should be ULTRA clean and NEVER repetitive, feel free to create helper functions and files as needed. Definitely make sure everything is clean and easy to understand. When there's some logic that is used in multiple places, create a helper function for it. In the end, write a human-readable README.md file in the email endpoint folder that explains how the email infrastructure works, meant to be read by a technical human (it should be VERY concise and understandable!)
[!NOTE] Introduce an Email Outbox pipeline (DB + worker) replacing direct sends; enqueue emails, batch-render, queue, and send with delivery stats and new endpoints.
DB & Schema:
- Add
EmailOutboxtable with comprehensive generated columns, constraints, and indexes; global metadata table for processing (EmailOutboxProcessingMetadata).- Migration to populate
EmailOutboxfrom legacySentEmail; removeSentEmailfrom schema.Email Pipeline:
- New queue step
runEmailQueueStepto claim, batch-render (per tenancy), queue, and send emails; handles retries, prioritization, scheduling, and status updates.- Dev cron runner script and internal endpoint
GET /api/latest/internal/email-queue-step(auth viaCRON_SECRET).API:
- Replace direct send with outbox via
sendEmailToMany; extend/emails/send-email(priority, scheduling, template/draft handling) and validate/resolve notification categories.- New
GET /api/latest/emails/delivery-info(windowed stats + capacity rate).- Internal emails CRUD now reads from
EmailOutbox.Rendering & Sending:
- Add batched Freestyle rendering for multiple emails/themes; new theme resolver API.
- Introduce low-level direct send utilities (SMTP/Resend) isolated from queue.
Seeds & Tests:
- Update seeds (email seeds mapped to outbox; session activity generator).
- Extensive e2e updates to await queued emails and cover delivery-info, queue step, and new flows.
Misc:
- Update OpenAPI gen to ESM import; tsup config to ESM; add dev cron, Docker compose additions; numerous package.json adjustments.
Written by Cursor Bugbot for commit 789872a39f89c02462419696580005da8e45dcd7. This will update automatically on new commits. Configure here.
Summary by CodeRabbit
-
New Features
- Email outbox pipeline, background queue step (cron-triggered) and direct low-level/bulk send capabilities.
- Email delivery stats & capacity metrics endpoint.
-
API Changes
- send-email responses now return user_id-only results; input validation tightened.
- New delivery-info GET endpoint; unsubscribe verification extended to include category info.
-
Documentation
- Added Email Infrastructure README.
-
Tests & Chores
- Many backend and e2e tests updated; package manifests updated with packageManager and docker/dev tooling additions.
βοΈ Tip: You can customize this high-level summary in your review settings.
The latest updates on your projects. Learn more about Vercel for GitHub.
| Project | Deployment | Preview | Comments | Updated (UTC) |
|---|---|---|---|---|
| stack-backend | Preview | Comment | Dec 12, 2025 6:06pm | |
| stack-dashboard | Preview | Comment | Dec 12, 2025 6:06pm | |
| stack-demo | Preview | Comment | Dec 12, 2025 6:06pm | |
| stack-docs | Preview | Comment | Dec 12, 2025 6:06pm |
[!NOTE]
Other AI code review bot(s) detected
CodeRabbit has detected other AI code review bot(s) in this pull request and will avoid duplicating their findings in the review comments. This may lead to a less comprehensive review.
Walkthrough
Replaces the legacy SentEmail flow with a full EmailOutbox system: schema + migrations, outbox processing worker, low-level provider layer, batched rendering, delivery statistics endpoint, API surface and SDK types, many e2e test updates, and supporting infra/scripts (cron, prisma client, Docker, CI).
Changes
| Cohort / File(s) | Summary |
|---|---|
DB schema & migrations apps/backend/prisma/schema.prisma, apps/backend/prisma/migrations/..._email_outbox/migration.sql, apps/backend/prisma/migrations/..._migrate_sent_email/migration.sql, apps/backend/prisma/migrations/..._add_no_email_provided_skip_reason/migration.sql |
Introduce EmailOutbox, EmailDraft, EmailOutboxProcessingMetadata models and enums; add generated columns, CHECK constraints, indices, and a migration to migrate data from SentEmail plus an enum extension. |
Outbox worker & orchestration apps/backend/src/lib/email-queue-step.tsx, apps/backend/scripts/run-email-queue.ts, apps/backend/src/app/api/latest/internal/email-queue-step/route.tsx, vercel.json |
Add runEmailQueueStep implementation, serialization helpers, periodic runner script, internal step route, and cron schedule for periodic execution. |
Low-level sending & emails API apps/backend/src/lib/emails-low-level.tsx, apps/backend/src/lib/emails.tsx, apps/backend/src/app/api/latest/emails/send-email/route.tsx |
New low-level provider layer, error mapping and retry semantics; replace per-user send with sendEmailToMany that writes EmailOutbox rows; tighten variables typing and unify template/draft/html handling. |
Rendering & templates apps/backend/src/lib/email-rendering.tsx, apps/backend/src/lib/email-rendering.test.tsx, apps/backend/src/app/api/latest/emails/render-email/route.tsx |
Add batched rendering API renderEmailsForTenancyBatched, rename theme lookup to getEmailThemeForThemeId, add tests covering batched rendering and template/theme errors. |
Delivery stats & API apps/backend/src/lib/email-delivery-stats.tsx, apps/backend/src/app/api/latest/emails/delivery-info/route.tsx, packages/template/src/lib/stack-app/email/index.ts, packages/template/src/lib/stack-app/apps/interfaces/server-app.ts, packages/template/src/lib/stack-app/apps/implementations/server-app-impl.ts |
Add delivery aggregation for hour/day/week/month, capacity/penalty calculations, new types, and server-app methods/hooks to expose stats. |
CRUD & internal tooling apps/backend/src/app/api/latest/internal/emails/crud.tsx, apps/backend/src/app/api/latest/internal/failed-emails-digest/*, apps/backend/src/app/api/latest/internal/send-test-email/route.tsx |
Migrate CRUD to EmailOutbox, adapt recipient resolution and error fields, disable/guard failed-emails-digest sending, and update internal test routes to new low-level API. |
Auth / verification / contact channels apps/backend/src/app/api/latest/auth/*, apps/backend/src/app/api/latest/contact-channels/*, apps/backend/src/app/api/latest/internal/send-sign-in-invitation/route.tsx |
Replace sendEmailFromTemplate with sendEmailFromDefaultTemplate (or low-level variants), add shouldSkipDeliverabilityCheck flag in many send paths, and remove legacy OTP type branching. |
Prisma client & stack factory apps/backend/src/prisma-client.tsx, apps/backend/src/stack.tsx, apps/backend/prisma/seed.ts |
Replace exported app instance with getStackServerApp(), add per-connection Prisma client cache, OrbStack TCP probe optimization, and update seed to create EmailOutbox entries. |
E2E helpers & tests apps/e2e/tests/helpers.ts, apps/e2e/tests/setup.ts, apps/e2e/tests/backend/backend-helpers.ts, apps/e2e/tests/backend/endpoints/api/v1/**/*, apps/e2e/tests/js/* |
Add mailbox wait helpers (waitForMessagesWithSubject / waitForMessagesWithSubjectCount), Auth.fastSignUp, test cleanup hooks, and update many tests to subject-based waits and new response shapes (results include user_id only). |
Utilities, results & robustness apps/backend/src/lib/freestyle.tsx, apps/backend/src/utils/telemetry.tsx, packages/stack-shared/src/utils/results.tsx, packages/stack-shared/src/utils/proxies.tsx, apps/backend/src/lib/upstash.tsx |
Add retry around Freestyle execution, await trace spans, make RetryError generic and retry typed, add throwingProxy, and minor upstash/dev bypass improvements. |
Infra, scripts & packaging apps/backend/package.json, package.json, .vscode/settings.json, docker/dependencies/docker.compose.yaml, vercel.json, .github/workflows/docker-server-build-run.yaml, turbo.json, apps/dev-launchpad/public/index.html |
Add type: "module" and run-email-queue script, VSCode cSpell word, Drizzle Gateway service and postgres init flag, cron entry, Docker CI workflow, env flags, and dev-launchpad entry; adjust startup delays. |
Public schema / SDK changes packages/stack-shared/src/interface/crud/emails.ts, packages/stack-shared/src/interface/server-interface.ts, packages/template/src/lib/stack-app/email/index.ts |
Remove sender_config from public sentEmailReadSchema, add getEmailDeliveryInfo client method and delivery types to SDK. |
Misc & cleanup examples/react-example/*, apps/backend/src/polyfills.tsx, apps/backend/.env*, .gitignore, apps/backend/src/app/api/latest/emails/README.md |
Remove example ESLint config and lint deps, guard Sentry sink, add STACK_DEFAULT_EMAIL_CAPACITY_PER_HOUR env, add .gitignore tweak, and add emails README. |
Sequence Diagram(s)
sequenceDiagram
participant Client as Client/API
participant DB as Postgres (EmailOutbox)
participant Worker as Queue Worker
participant Renderer as Template Renderer (Freestyle)
participant Provider as Email Provider
participant Stats as Delivery Stats
Client->>DB: POST /emails -> create EmailOutbox rows (PREPARING)
Worker->>DB: claim rows for rendering -> mark RENDERING
Worker->>Renderer: render tsxSource + theme
Renderer-->>Worker: renderedHtml, renderedText, subject
Worker->>DB: update EmailOutbox (SCHEDULED / QUEUED)
Worker->>DB: claim queued emails -> mark SENDING (startedSendingAt)
Worker->>Provider: deliver email(s)
Provider-->>Worker: response (success/failure)
Worker->>DB: update sent/delivery/error/delivery timestamps
Provider->>Stats: webhook events (bounced/opened/clicked)
Stats->>DB: persist delivery metrics for tenancy
Estimated code review effort
π― 5 (Critical) | β±οΈ ~120 minutes
Areas needing careful review:
-
apps/backend/prisma/migrations/..._email_outbox/migration.sqlβ generated columns, CHECK constraints, indices, idempotence. -
apps/backend/prisma/migrations/..._migrate_sent_email/migration.sqlβ data mapping and ON CONFLICT safety. -
apps/backend/src/lib/email-queue-step.tsxβ complex orchestration, two-pass rendering, recipient resolution and state transitions. -
apps/backend/src/lib/emails.tsx&apps/backend/src/lib/emails-low-level.tsxβ send semantics, retries, deliverability checks, and provider error mapping. -
apps/backend/src/prisma-client.tsx&apps/backend/src/stack.tsxβ initialization, caching, and migration togetStackServerApp. - E2E test changes β mailbox helpers, new auth helpers, and timing/stability across many tests.
Possibly related PRs
- stack-auth/stack-auth#849 β related: EmailDraft additions and draft/outbox integrations (migrations and route interactions).
-
stack-auth/stack-auth#893 β related: mailbox wait helpers and e2e mailbox/test adjustments (
waitForMessagesWithSubject/Count). -
stack-auth/stack-auth#879 β related: changes to stack app and Prisma initialization patterns (
getStackServerApp/ prisma client refactor).
Poem
π° I hopped from SentEmail to Outbox land,
Queued my carrots, queued them by hand.
Rendered, sent, and counted each hop,
Bounced or opened β I never stop.
Cheers β a rabbit on the email job! π₯π¬
Pre-merge checks and finishing touches
β Failed checks (1 warning)
| Check name | Status | Explanation | Resolution |
|---|---|---|---|
| Docstring Coverage | β οΈ Warning | Docstring coverage is 7.89% which is insufficient. The required threshold is 80.00%. | You can run @coderabbitai generate docstrings to improve docstring coverage. |
β Passed checks (2 passed)
| Check name | Status | Explanation |
|---|---|---|
| Title check | β Passed | The title 'Email outbox backend' is specific and clearly describes the main architectural changeβmigration to an async outbox pipeline for email sending. It accurately reflects the most important change in the PR. |
| Description check | β Passed | The description is comprehensive and well-structured, providing context, implementation strategy, guidance for reviewers, and a detailed prompt explaining the architecture. It covers motivation, key files to review, and the scope of changes. |
β¨ Finishing touches
π§ͺ Generate unit tests (beta)
- [ ] Create PR with unit tests
- [ ] Post copyable unit tests in a comment
- [ ] Commit unit tests in branch
email-outbox-backend
π Recent review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
π₯ Commits
Reviewing files that changed from the base of the PR and between 1a8332281481ada011c67c3410e5c71f5d4dbaf6 and 313c229d09ddc5ab12b5d27be1a4244ad64ae7dc.
π Files selected for processing (1)
-
apps/backend/prisma/migrations/20251212180000_email_outbox/migration.sql(1 hunks)
π§ Files skipped from review as they are similar to previous changes (1)
- apps/backend/prisma/migrations/20251212180000_email_outbox/migration.sql
β° Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (12)
- GitHub Check: build (22.x)
- GitHub Check: build (22.x)
- GitHub Check: lint_and_build (latest)
- GitHub Check: build (22.x)
- GitHub Check: setup-tests
- GitHub Check: restart-dev-and-test
- GitHub Check: restart-dev-and-test-with-custom-base-port
- GitHub Check: all-good
- GitHub Check: Vercel Agent Review
- GitHub Check: Cursor Bugbot
- GitHub Check: check_prisma_migrations (22.x)
- GitHub Check: docker
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.
Comment @coderabbitai help to get the list of available commands and usage tips.
Greptile Overview
Greptile Summary
This PR replaces the direct email sending approach with a robust EmailOutbox-based queue system that separates rendering, queueing, and sending into distinct phases. The new infrastructure provides better observability, retry logic, capacity management, and deliverability tracking.
Major Changes
-
Database Schema: New
EmailOutboxtable with comprehensive constraints, generated status columns, and proper indexes for queue processing. LegacySentEmaildata successfully migrated. -
Queue Processor:
runEmailQueueStep()orchestrates the email pipeline with atomic operations, proper locking, and per-tenancy capacity rate limiting using stochastic quotas. - Email Rendering: Batched Freestyle rendering with two-pass logic for unsubscribe links, proper theme resolution.
- Email Sending: Low-level SMTP with Emailable deliverability checks, comprehensive error categorization, and retry logic.
- API Endpoints: New delivery stats endpoint, secured cron endpoint, updated send-email to use queue.
Issues Found
-
Critical Bug:
calculateCapacityRate()inapps/backend/src/lib/email-delivery-stats.tsx:32has incorrect formula multiplying monthly sent count by hours instead of using it directly as a scaling factor
Confidence Score: 3/5
- Safe to merge after fixing the capacity rate calculation bug
- The infrastructure is well-designed with proper atomicity, locking, and error handling. However, the critical bug in capacity rate calculation (line 32 of email-delivery-stats.tsx) will cause incorrect email throughput limiting. The formula
8 * 30 * 24 * stats.month.sentappears to multiply sent emails by hours, when it should likely be8 * stats.month.sent. This affects production email delivery rates. - apps/backend/src/lib/email-delivery-stats.tsx requires immediate fix to the capacity calculation formula
Important Files Changed
File Analysis
| Filename | Score | Overview |
|---|---|---|
| apps/backend/prisma/schema.prisma | 4/5 | Adds comprehensive EmailOutbox table with generated columns for status tracking, constraints enforce data integrity, proper indexes for queue processing |
| apps/backend/src/lib/email-queue-step.tsx | 4/5 | Implements robust queue processing with rendering, queueing, and sending phases; uses proper locking and atomic updates, handles edge cases well |
| apps/backend/src/lib/email-delivery-stats.tsx | 2/5 | Contains critical bug in capacity rate calculation formula (line 32) that incorrectly multiplies monthly sent count by hours |
| apps/backend/src/lib/emails.tsx | 5/5 | Clean refactoring to use EmailOutbox queue system, proper recipient type handling, good separation of concerns |
| apps/backend/src/lib/emails-low-level.tsx | 5/5 | Well-structured low-level email sending with proper error handling, retry logic, and Emailable integration for deliverability checks |
Sequence Diagram
sequenceDiagram
participant Cron as Vercel Cron
participant API as /email-queue-step
participant Queue as runEmailQueueStep()
participant DB as EmailOutbox DB
participant Render as Email Renderer
participant Freestyle as Freestyle API
participant Send as Email Sender
participant SMTP as SMTP Server
Cron->>API: GET (every minute)
API->>Queue: runEmailQueueStep()
Note over Queue,DB: Phase 1: Update Execution Time
Queue->>DB: Update lastExecutedAt
DB-->>Queue: Return delta seconds
Note over Queue,DB: Phase 2: Claim & Render Emails
Queue->>DB: SELECT unrendered emails (LIMIT 50)
DB-->>Queue: Return claimed rows
Queue->>Render: renderTenancyEmails(rows)
Render->>DB: Fetch user data
Render->>Freestyle: Batch render templates
Freestyle-->>Render: Rendered HTML/text/subject
Render->>DB: UPDATE rendered fields
Note over Queue,DB: Phase 3: Queue Ready Emails
Queue->>DB: UPDATE isQueued=true WHERE scheduledAt <= NOW()
Note over Queue,DB: Phase 4: Prepare Send Plan
Queue->>DB: Get distinct tenancy IDs
loop For each tenancy
Queue->>DB: Get delivery stats
Queue->>Queue: Calculate capacity rate
Queue->>DB: Claim emails (stochastic quota)
end
Note over Queue,DB: Phase 5: Send Emails
loop For each tenancy batch
Queue->>DB: Fetch user data
par For each email
Queue->>Send: processSingleEmail()
Send->>DB: Check user exists
Send->>DB: Check notification preferences
Send->>SMTP: Send via provider
SMTP-->>Send: Success/Error
Send->>DB: UPDATE finishedSendingAt, errors
end
end
Queue-->>API: Complete
API-->>Cron: 200 OK
Older cmux preview screenshots (latest comment is below)
Preview Screenshots
β³ Preview screenshots are being captured...
Workspace and dev browser links will appear here once the preview environment is ready.
Generated by cmux preview system
Older cmux preview screenshots (latest comment is below)
Preview Screenshots
β³ Preview screenshots are being captured...
Workspace and dev browser links will appear here once the preview environment is ready.
Generated by cmux preview system
Older cmux preview screenshots (latest comment is below)
Preview Screenshots
β³ Preview screenshots are being captured...
Workspace and dev browser links will appear here once the preview environment is ready.
Generated by cmux preview system
Older cmux preview screenshots (latest comment is below)
Preview Screenshots
β³ Preview screenshots are being captured...
Workspace and dev browser links will appear here once the preview environment is ready.
Generated by cmux preview system
Older cmux preview screenshots (latest comment is below)
Preview Screenshots
β³ Preview screenshots are being captured...
Workspace and dev browser links will appear here once the preview environment is ready.
Generated by cmux preview system
Older cmux preview screenshots (latest comment is below)
Preview Screenshots
β³ Preview screenshots are being captured...
Workspace and dev browser links will appear here once the preview environment is ready.
Generated by cmux preview system
Older cmux preview screenshots (latest comment is below)
Preview Screenshots
β³ Preview screenshots are being captured...
Workspace and dev browser links will appear here once the preview environment is ready.
Generated by cmux preview system
Older cmux preview screenshots (latest comment is below)
Preview Screenshots
β³ Preview screenshots are being captured...
Workspace and dev browser links will appear here once the preview environment is ready.
Generated by cmux preview system
Older cmux preview screenshots (latest comment is below)
Preview Screenshots
β³ Preview screenshots are being captured...
Workspace and dev browser links will appear here once the preview environment is ready.
Generated by cmux preview system
Older cmux preview screenshots (latest comment is below)
Preview Screenshots
β³ Preview screenshots are being captured...
Workspace and dev browser links will appear here once the preview environment is ready.
Generated by cmux preview system
Older cmux preview screenshots (latest comment is below)
Preview Screenshots
β³ Preview screenshots are being captured...
Workspace and dev browser links will appear here once the preview environment is ready.
Generated by cmux preview system
Older cmux preview screenshots (latest comment is below)
Preview Screenshots
β³ Preview screenshots are being captured...
Workspace and dev browser links will appear here once the preview environment is ready.
Generated by cmux preview system
Older cmux preview screenshots (latest comment is below)
Preview Screenshots
β³ Preview screenshots are being captured...
Workspace and dev browser links will appear here once the preview environment is ready.
Generated by cmux preview system
Older cmux preview screenshots (latest comment is below)
Preview Screenshots
β³ Preview screenshots are being captured...
Workspace and dev browser links will appear here once the preview environment is ready.
Generated by cmux preview system
Older cmux preview screenshots (latest comment is below)
Preview Screenshots
β³ Preview screenshots are being captured...
Workspace and dev browser links will appear here once the preview environment is ready.
Generated by cmux preview system
Older cmux preview screenshots (latest comment is below)
Preview Screenshots
β³ Preview screenshots are being captured...
Workspace and dev browser links will appear here once the preview environment is ready.
Generated by cmux preview system
Older cmux preview screenshots (latest comment is below)
Preview Screenshots
β³ Preview screenshots are being captured...
Workspace and dev browser links will appear here once the preview environment is ready.
Generated by cmux preview system
Older cmux preview screenshots (latest comment is below)
Preview Screenshots
β³ Preview screenshots are being captured...
Workspace and dev browser links will appear here once the preview environment is ready.
Generated by cmux preview system
Older cmux preview screenshots (latest comment is below)
Preview Screenshots
β³ Preview screenshots are being captured...
Workspace and dev browser links will appear here once the preview environment is ready.
Generated by cmux preview system
Older cmux preview screenshots (latest comment is below)
Preview Screenshots
β³ Preview screenshots are being captured...
Workspace and dev browser links will appear here once the preview environment is ready.
Generated by cmux preview system
Older cmux preview screenshots (latest comment is below)
Preview Screenshots
β³ Preview screenshots are being captured...
Workspace and dev browser links will appear here once the preview environment is ready.
Generated by cmux preview system
Older cmux preview screenshots (latest comment is below)
Preview Screenshots
β³ Preview screenshots are being captured...
Workspace and dev browser links will appear here once the preview environment is ready.
Generated by cmux preview system
Older cmux preview screenshots (latest comment is below)
Preview Screenshots
β³ Preview screenshots are being captured...
Workspace and dev browser links will appear here once the preview environment is ready.
Generated by cmux preview system
Older cmux preview screenshots (latest comment is below)
Preview Screenshots
β³ Preview screenshots are being captured...
Workspace and dev browser links will appear here once the preview environment is ready.
Generated by cmux preview system
Older cmux preview screenshots (latest comment is below)
Preview Screenshots
β³ Preview screenshots are being captured...
Workspace and dev browser links will appear here once the preview environment is ready.
Generated by cmux preview system
Older cmux preview screenshots (latest comment is below)
Preview Screenshots
β³ Preview screenshots are being captured...
Workspace and dev browser links will appear here once the preview environment is ready.
Generated by cmux preview system
Preview Screenshots
β³ Preview screenshots are being captured...
Workspace and dev browser links will appear here once the preview environment is ready.
Generated by cmux preview system