The pipeline
The guarantee: at least once
The outbox row is written inside the same database transaction as the domain change. So:No event without the change
If the approval rolls back, the outbox row rolls back with it. You never receive a webhook for something that did not happen.
No change without the event
If the approval commits, the outbox row committed too. The event cannot be lost to a process restart — it is a durable row, not an in-memory retry timer.
Retries
A delivery is a success when your endpoint returns any
2xx. Anything else — a non-2xx status, a connection failure, a timeout — is a failure and is retried.
The dead-letter queue
An event that exhausts all eight attempts moves toFailed. It is not deleted.
- It appears in the delivery log in the admin console.
- Every
api:manageholder receives an in-app notification. - It can be replayed — re-queued, sending the original payload unchanged.
Crash recovery
A delivery interrupted mid-flight — the OnTime process restarting between claiming a row and completing it — is reclaimed after a few minutes and retried. The event is not stranded. Claiming uses a database-level lock, so concurrent workers cannot pick up the same row.What else the tick does
The same background tick drains the outbox, runs due scheduled reports, checks for due leave accruals, closes finished days, and detects offline devices.There is no separate queue broker, worker service or cron service in OnTime’s topology. This matters for one reason: an event’s durability comes from the database row, not from a message broker. A deploy in the middle of a retry cycle loses nothing.