How to Poll Transactional Email Delivery Status in Node.js: Cron Without Webhooks
Short answer: poll transactional email delivery status from a Node.js cron worker through the events API, but keep the password-reset template and token policy in your application. Polling is evidence collection, not authorization, and the reset token must expire independently of delivery state. I distrust dashboards because they rarely tell me what page fired. A bounded poll is easier to reason…
To determine the delivery status of transactional emails sent via Node.js without using webhooks, you should implement a cron worker. This worker should poll the email delivery status through the events API, but remember that polling is simply a method of collecting evidence, not a way to authorize or confirm the status of the password reset process. The password reset token must still expire on its own, regardless of the delivery status.
It's advisable to avoid relying on dashboards for this purpose, as they often do not accurately indicate which page triggered the email. Instead, focus on the underlying signal: which page fired and whether the learner's action can still be affected. When recording email delivery status, ensure you capture an immutable send attempt, internal attempt ID, provider message ID, template revision, and a digest of the rendered email body.
Provider labels like queued, delivered, deferred, or bounced indicate transport status, but they do not prove that a password has been changed.
Set a 15-minute token expiration as a security measure, not as the polling interval. Begin by polling once every minute for ten minutes, then cease observing. Remember that late delivery receipts are merely audit data and should not be used to revive the link.
When reconciling events without webhooks, use a bounded overlap window. Each cron run should read attempts whose last check occurred long enough ago and whose observation deadline has not passed yet. Normalize provider events into your own state machine, using event IDs to write data and leave an attempt as due when the read operation fails. It's crucial to record a failed read as a successful check to avoid creating silent gaps in your data.
The reconciliation process requires a reader interface and a store interface. The reader should retrieve events given a context, provider ID, and time. The store should return attempts due at a specific time, record a new event, and mark an attempt as checked at a given time. The reconciliation function reads attempts from the store, fetches related events from the reader, records each event in the store, and marks the attempt as checked.
A scheduler running every minute, with a database lease to prevent overlapping workers, is recommended.
In terms of implementation, consider this Node.js scheduling concern even if the reconciliation core is a small Go binary. The choice of runtime is a maintenance trade-off, not a guarantee of delivery. Focus on the following boundaries: template ownership lies with the application team, security is responsible for expiry and single-use checks, and the transport adapter handles credentials, provider IDs, and pagination.
Render the email once at send time and persist the template revision and digest. Do not rewrite history with subsequent copy edits.
Protect personal data by only recording an attempt ID and provider ID to correlate results. Adhere to FTC's CAN-SPAM guidelines for commercial streams to ensure honest headers and compliance. When polling is not suitable, such as when sub-minute reactions are required or provider-side event volume is high, opt for a push path instead.
Always verify verification and rollback procedures before notifying anyone. Test various scenarios, including queued-to-delivered, deferred-to-bounced, duplicate events, and events missing until the deadline. Assert that late deliveries do not extend token validity. Start by running in "shadow mode," which reads events and writes audit records without changing user state.
To roll back, simply disable the scheduler and retain the immutable attempts. Lastly, the reset endpoint must independently check token expiry, usage, and account binding; delivery status can provide support information but cannot authorize a password change.
Written by urgent.news from Dev.to's reporting — not their text. Machine-written — may contain errors; check the original before relying on it.