Getting Started

REST API: Campaigns

Affiliates

Conversions

Affiliate Teams

SCALE

Coupons

Payouts

Other

Webhooks

MCP Server (AI)

← All guides

Auto-approve commissions

Build a background job that approves conversions matching your business rules. Reduces manual review overhead while keeping human-in-the-loop for edge cases.

Intermediate

10 min read

Ultimate plan

Example rules to auto-approve

  • Sale was more than 7 days ago (refund window closed)
  • Customer email domain matches known-good list
  • Amount is below a low-risk threshold (e.g. $50)
  • Affiliate has a track record of clean conversions

Cron job skeleton

// 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://www.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, needsReview = 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++;
    else if (patch.status === 409) {
      const { error } = await patch.json();
      // commission_above_sale: the commission is more than 5 times its sale.
      // Nothing was approved. Leave it for a person; never resend with
      // confirmAboveSale from a job.
      if (error?.code === 'commission_above_sale') needsReview++;
    }

    await new Promise(r => setTimeout(r, 250)); // rate-limit courtesy
  }

  return Response.json({ approved, skipped, needsReview, 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;
}

Safety tips

  • Always run a dry-run first, log what would be approved before enabling writes
  • Keep a manual override. UI button to reject an already-approved conversion (endpoint accepts rejected as long as it's not paid yet)
  • Alert on anomalies, if more than 5% of approvals fail your rules, something changed upstream
  • Never auto-approve conversions where fraud detection flagged status: 'rejected'
  • A commission more than 5 times its sale is refused with 409 commission_above_sale and nothing changes. Send those to a person; a job should never set confirmAboveSale for them

Full reference: PATCH /api/v1/conversions/:id