Documentation Index
Fetch the complete documentation index at: https://docs.aspfox.com/llms.txt
Use this file to discover all available pages before exploring further.
Jobs overview
AspFox runs four recurring background jobs via Hangfire with PostgreSQL storage.Trial Expiry Job
Schedule: Daily at 9:00 AM UTC What it does: Scans all tenants on a Pro or Business trial. For tenants whose trial ends within 7 days, sends a 7-day warning email. For tenants whose trial ends within 1 day, sends a 1-day warning email. For tenants whose trial has already expired, sets subscription status toCanceled and downgrades to the Free plan.
Why it exists: Stripe can handle trial expiry, but local state needs to reflect the change. Running this job ensures local subscription state stays accurate even if a Stripe webhook is missed.
Retry behavior: 3 attempts with 60, 300, and 900 second delays between attempts (1 minute, 5 minutes, 15 minutes). If all three attempts fail, the job is moved to Hangfire’s Failed queue and appears in the dashboard.
Batch size: Processes 100 tenants per run. If you have more than 100 tenants in trial, the remainder are processed on the next run.
Subscription Sync Job
Schedule: Every 2 hours What it does: Fetches all active Stripe subscriptions for all tenants and compares them to local state. If they differ, updates local state to match Stripe. This reconciles missed webhooks. Why it exists: Stripe webhooks can fail — your server might be down, your endpoint might return an error, or a network issue might prevent delivery. This job is the safety net. Any divergence between Stripe state and local state is corrected within 2 hours. Retry behavior: Same as above — 3 attempts, exponential backoff. Batch size: All tenants with a non-nullStripeSubscriptionId. Calls StripeService.GetSubscriptionAsync() for each, so be aware that a large number of tenants will make many Stripe API calls.
Token Cleanup Job
Schedule: Daily at 3:00 AM UTC What it does: Deletes expired records from four tables:refresh_tokens, password_reset_tokens, email_verification_tokens, and magic_link_tokens. Only deletes records where ExpiresAt < NOW() and (for refresh tokens) IsRevoked = true OR ExpiresAt < NOW().
Why it exists: Without cleanup, these tables grow indefinitely. A user who changes their password frequently accumulates rows in password_reset_tokens. Cleanup keeps the tables small and queries fast.
Invitation Cleanup Job
Schedule: Weekly on Sunday at 4:00 AM UTC What it does: Removes expired invitations older than 30 days. Invitations expired within the last 30 days are kept — they appear in the UI with a “Resend” button. Invitations expired more than 30 days ago have no practical use and are purged.Hangfire dashboard
The Hangfire dashboard is available at/hangfire (e.g., http://localhost:5000/hangfire locally). Access requires the is_admin JWT claim.
From the dashboard you can:
- View all recurring jobs and their last run time and next scheduled run
- View the queue of pending jobs
- See failed jobs with full stack traces
- Retry failed jobs manually
- Delete failed jobs
- Trigger any recurring job to run immediately (useful after fixing a bug that caused failures)
How to add a new job
[DisableConcurrentExecution] prevents the same job from running twice simultaneously if one run takes longer than the scheduled interval. The timeout value is how long Hangfire waits to acquire the distributed lock before skipping the run.