Build a background job that approves conversions matching your business rules. Reduces manual review overhead while keeping human-in-the-loop for edge cases.
10 min read
// app/api/cron/auto-approve/route.ts
// Runs every hour via vercel.json: { "crons": [{ "path": "/api/cron/auto-approve", "schedule": "0 * * * *" }] }
const KEY = process.env.LINKJOLT_API_KEY!;
const LJ = 'https://linkjolt.io/api/v1';
export async function GET(req: Request) {
// Verify this is a real Vercel cron call
if (req.headers.get('authorization') !== `Bearer ${process.env.CRON_SECRET}`) {
return new Response('Unauthorized', { status: 401 });
}
// Pull pending conversions older than 7 days
const sevenDaysAgo = new Date(Date.now() - 7 * 24 * 60 * 60 * 1000).toISOString();
const res = await fetch(`${LJ}/conversions?status=pending&to=${sevenDaysAgo}&limit=100`, {
headers: { Authorization: `Bearer ${KEY}` },
});
const { data: pending } = await res.json();
let approved = 0, skipped = 0;
for (const c of pending) {
if (!shouldApprove(c)) { skipped++; continue; }
const patch = await fetch(`${LJ}/conversions/${c.id}`, {
method: 'PATCH',
headers: {
'Authorization': `Bearer ${KEY}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({ status: 'approved' }),
});
if (patch.ok) approved++;
await new Promise(r => setTimeout(r, 250)); // rate-limit courtesy
}
return Response.json({ approved, skipped, total: pending.length });
}
function shouldApprove(c: any): boolean {
// Your business rules here
if (c.amount > 500) return false; // Over $500 — manual review
if (c.conversionType === 'recurring') return true; // Recurring = low risk
return true;
}rejected as long as it's not paid yet)status: 'rejected'Full reference: PATCH /api/v1/conversions/:id