Cron Jobs Setup
Configure automated credit grants and expiration with various cron job platforms
📋 Overview
The credits system requires two automated cron jobs to function properly:
- Grant Credits Job - Distributes monthly credits to subscription users
- Expire Credits Job - Processes expired credit batches
Both jobs must be configured to call your API endpoints with proper authentication.
🔐 Authentication
All cron job requests must include the CRON_SECRET
in the Authorization header:
Authorization: Bearer your-cron-secret-here
Generating CRON_SECRET
Generate a secure random string (16+ characters):
# Using OpenSSL
openssl rand -hex 16
# Using Node.js
node -e "console.log(require('crypto').randomBytes(16).toString('hex'))"
# Or use any password generator
Add to your .env
file:
CRON_SECRET=a1b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6
🔄 Cron Job Endpoints
1. Grant Credits Endpoint
URL: https://yourdomain.com/api/jobs/credits/grant
Purpose: Grants monthly subscription credits to eligible users
Schedule: Daily at 00:00 UTC (midnight UTC)
HTTP Method: GET
Headers:
Authorization: Bearer your-cron-secret
Response:
{
"success": true,
"jobId": "550e8400-e29b-41d4-a716-446655440000",
"date": "2024-01-15",
"duration": 1250,
"results": {
"processed": 150,
"successful": 148,
"skipped": 2,
"failed": 0
}
}
2. Expire Credits Endpoint
URL: https://yourdomain.com/api/jobs/credits/expire
Purpose: Processes expired credit batches and updates balances
Schedule: Daily at 01:00 UTC (1 AM UTC)
HTTP Method: GET
Headers:
Authorization: Bearer your-cron-secret
Response:
{
"success": true,
"jobId": "660e8400-e29b-41d4-a716-446655440001",
"duration": 850,
"results": {
"processedBatches": 45,
"processedUsers": 38,
"totalExpiredAmount": 1250
}
}
🚀 Platform Setup Guides
Option 1: Vercel Cron Jobs
Best for: Applications deployed on Vercel
Setup Steps
- Create
vercel.json
in your project root:
{
"crons": [
{
"path": "/api/jobs/credits/grant",
"schedule": "0 0 * * *"
},
{
"path": "/api/jobs/credits/expire",
"schedule": "0 1 * * *"
}
]
}
- Add environment variable in Vercel:
Go to your project → Settings → Environment Variables:
- Key:
CRON_SECRET
- Value:
your-generated-secret
- Deploy your changes:
git add vercel.json
git commit -m "Add cron jobs configuration"
git push
- Verify in Vercel Dashboard:
Go to your project → Deployments → Cron Jobs to see active jobs.
Cron Schedule Format:
0 0 * * *
= Every day at 00:00 UTC0 1 * * *
= Every day at 01:00 UTC
Important Notes:
- ✅ Available on Vercel Pro plan and above
- ✅ Automatic authentication with Vercel
- ✅ Built-in monitoring and logs
- ⚠️ Maximum execution time: 10 seconds (Hobby), 60 seconds (Pro), 300 seconds (Enterprise)
Option 2: Cloudflare Scheduled Workers
Best for: Applications deployed on Cloudflare Pages/Workers
Setup Steps
- Create a new Worker in Cloudflare Dashboard:
Navigate to Workers & Pages → Create Worker
- Configure the Worker code:
// grant-credits-worker.js
export default {
async scheduled(event, env, ctx) {
const response = await fetch('https://yourdomain.com/api/jobs/credits/grant', {
method: 'GET',
headers: {
'Authorization': `Bearer ${env.CRON_SECRET}`,
},
});
const data = await response.json();
console.log('Grant credits job completed:', data);
},
};
- Add Cron Trigger:
In Worker settings → Triggers → Cron Triggers:
- Schedule:
0 0 * * *
- Set environment variable:
In Worker settings → Variables:
- Variable name:
CRON_SECRET
- Value:
your-generated-secret
- Repeat for expire credits job:
Create another Worker with schedule 0 1 * * *
pointing to the expire endpoint.
Important Notes:
- ✅ Free tier available with 100,000 requests/day
- ✅ Global edge network
- ✅ Reliable execution
- ⚠️ Requires separate Worker for each cron job
Option 3: cron-job.org
Best for: Any deployment platform, simple setup
Setup Steps
- Sign up at cron-job.org
Free account allows up to 50 cron jobs.
- Create first cron job (Grant Credits):
Click "Create Cronjob" and configure:
General Settings:
- Title:
Grant Monthly Credits
- Address:
https://yourdomain.com/api/jobs/credits/grant
- Request method:
GET
Schedule:
- Execution time:
00:00
(midnight) - Timezone:
UTC
- Days: Every day
Advanced Settings:
- HTTP Headers:
Authorization: Bearer your-cron-secret
Notifications:
- Enable email notifications for failures (recommended)
- Create second cron job (Expire Credits):
Repeat with:
- Title:
Expire Credits
- Address:
https://yourdomain.com/api/jobs/credits/expire
- Execution time:
01:00
- Save and activate:
Click "Create" and ensure the status shows as "Enabled".
Important Notes:
- ✅ Free tier available
- ✅ Email notifications for failures
- ✅ Execution history and logs
- ✅ Works with any platform
- ⚠️ Less reliable than platform-native solutions
- ⚠️ Requires manual setup for each environment
Option 4: Upstash QStash
Best for: Modern serverless applications, high reliability
Setup Steps
- Sign up at Upstash
Navigate to QStash section.
- Get your QStash token:
Copy your QStash Publishing Token from the dashboard.
- Create scheduled requests:
Using the QStash API or Dashboard:
Grant Credits Schedule:
curl -X POST "https://qstash.upstash.io/v2/schedules" \
-H "Authorization: Bearer YOUR_QSTASH_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"destination": "https://yourdomain.com/api/jobs/credits/grant",
"cron": "0 0 * * *",
"headers": {
"Authorization": "Bearer your-cron-secret"
}
}'
Expire Credits Schedule:
curl -X POST "https://qstash.upstash.io/v2/schedules" \
-H "Authorization: Bearer YOUR_QSTASH_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"destination": "https://yourdomain.com/api/jobs/credits/expire",
"cron": "0 1 * * *",
"headers": {
"Authorization": "Bearer your-cron-secret"
}
}'
- Verify in Upstash Dashboard:
Go to QStash → Schedules to see your configured jobs.
Important Notes:
- ✅ Free tier: 500 messages/day
- ✅ Built-in retries and dead letter queues
- ✅ Excellent reliability
- ✅ Request/response logs
- ✅ Works with any platform
- ⚠️ Requires API setup
Option 5: GitHub Actions (Not Recommended for Production)
Best for: Development/testing only
Setup Steps
- Create
.github/workflows/cron-jobs.yml
:
name: Credits Cron Jobs
on:
schedule:
# Grant credits daily at 00:00 UTC
- cron: '0 0 * * *'
# Expire credits daily at 01:00 UTC
- cron: '0 1 * * *'
workflow_dispatch: # Allow manual trigger
jobs:
grant-credits:
if: github.event.schedule == '0 0 * * *'
runs-on: ubuntu-latest
steps:
- name: Grant Monthly Credits
run: |
curl -X GET "https://yourdomain.com/api/jobs/credits/grant" \
-H "Authorization: Bearer ${{ secrets.CRON_SECRET }}"
expire-credits:
if: github.event.schedule == '0 1 * * *'
runs-on: ubuntu-latest
steps:
- name: Expire Credits
run: |
curl -X GET "https://yourdomain.com/api/jobs/credits/expire" \
-H "Authorization: Bearer ${{ secrets.CRON_SECRET }}"
- Add secret to GitHub:
Repository → Settings → Secrets → New repository secret:
- Name:
CRON_SECRET
- Value:
your-generated-secret
Why Not Recommended:
- ⚠️ Less reliable (can be delayed or skipped)
- ⚠️ Requires public repository or GitHub Pro
- ⚠️ Not designed for critical operations
- ⚠️ Rate limits may apply
🔍 Monitoring & Debugging
Check Cron Job Execution
Each job returns a unique jobId
for tracking. Monitor your application logs:
# Vercel
vercel logs --follow
# Cloudflare
wrangler tail
# Check application logs for:
[Credits Grant Job abc123] Starting daily grant process
[Credits Grant Job abc123] Completed in 1250ms. Processed: 150, Successful: 148
Common Issues
1. Authentication Failed
Error: { "error": "Unauthorized" }
Solution:
- Verify
CRON_SECRET
environment variable is set - Check the Authorization header format:
Bearer your-secret
- Ensure no extra spaces or newlines in the secret
2. Job Timeout
Error: Job times out or returns 504
Solutions:
- Increase execution timeout in your platform settings
- Optimize database queries
- Process users in smaller batches
- Consider breaking into multiple jobs
3. Duplicate Grants
Symptom: Users receive credits multiple times
Prevention:
- The system automatically prevents duplicates using
referenceId
- Check
grantPeriod
uniqueness in database - Ensure only one cron job is configured per endpoint
4. No Credits Granted
Check:
// Verify subscription configuration
console.log(appConfig.credits.subscription);
// Check active subscriptions
const purchases = await db
.select()
.from(purchase)
.where(eq(purchase.status, 'active'));
console.log(`Active subscriptions: ${purchases.length}`);
Manual Testing
Test the endpoints manually:
# Test grant endpoint
curl -X GET "https://yourdomain.com/api/jobs/credits/grant" \
-H "Authorization: Bearer your-cron-secret"
# Test expire endpoint
curl -X GET "https://yourdomain.com/api/jobs/credits/expire" \
-H "Authorization: Bearer your-cron-secret"
📊 Best Practices
1. Timing
Recommended Schedule:
- Grant: 00:00 UTC - Start of day ensures credits available early
- Expire: 01:00 UTC - After grants, prevents race conditions
Why separate times?
- Prevents conflicting database updates
- Grants process first, then expiration cleanup
- Easier to debug if issues occur
2. Monitoring
Set up alerts for:
- ✅ Job execution failures
- ✅ Response time > 5 seconds
- ✅ Error rate > 1%
- ✅ No successful execution for 24 hours
3. Backup Strategy
Configure redundant cron jobs on different platforms:
- Primary: Vercel Cron Jobs (if on Vercel)
- Backup: Upstash QStash or cron-job.org
If primary fails, backup ensures continuity.
4. Database Connection
Ensure your database can handle concurrent connections:
// In production, use connection pooling
const pool = new Pool({
max: 20, // Maximum connections
idleTimeoutMillis: 30000,
connectionTimeoutMillis: 2000,
});
🔐 Security Best Practices
- Rotate CRON_SECRET regularly (every 90 days)
- Use different secrets for production and staging
- Monitor for unauthorized requests:
if (authHeader !== `Bearer ${process.env.CRON_SECRET}`) { console.error(`[Security] Unauthorized cron attempt from ${request.ip}`); // Consider implementing rate limiting }
- Implement IP whitelist (if platform supports):
const allowedIPs = ['1.2.3.4', '5.6.7.8']; if (!allowedIPs.includes(request.ip)) { return NextResponse.json({ error: 'Forbidden' }, { status: 403 }); }
📚 Related Documentation
- 🎯 Credits System Overview - Understand the credits system
- 🔧 API Reference - Detailed API documentation
- 💡 Usage Examples - Implementation examples