Skills로 돌아가기
mbfinotti/partnerships-skills검사 통과

SKILL DETAIL

affiliate-payout-audit

mbfinotti/partnerships-skills/affiliate-payout-audit

Audit an affiliate or partner commission payout run for accuracy before money is disbursed - per-line rate and tier recomputation, double-counted conversions, clawback and validation windows, period cutoff, currency, rounding, and tax-form status, delivered as an audit report with severities and recommended holds. Covers B2B SaaS recurring revenue-share and B2C ecommerce order-level CPA programs. Use whenever the user mentions an affiliate payout audit, commission accuracy, payout reconciliation, overpaid or underpaid affiliates, or a pre-disbursement review, even if they never say audit. Do NOT use for spotting fraudulent affiliates - use mbfinotti/partnerships-skills@affiliate-fraud-detection instead.

설치 수 · 529출처 보기

Installation

npx skills add https://github.com/mbfinotti/partnerships-skills --skill affiliate-payout-audit

스킬 파일

SKILL.md

최근 동기화 · 2026. 10. 23.

evals/evals.json›
{
  "skill_name": "affiliate-payout-audit",
  "evals": [
    {
      "id": 1,
      "prompt": "I'm closing out the February payout for our Thistlewood Home affiliate program and I want a second pair of eyes before I hit release on Friday. Our network export shows 1,904 conversions on a 7-day attribution window, our own in-house tracker shows 1,671 on a 30-day window, so I've been reporting 3,575 conversions to the exec team. Proposed payout is $31,406.18. My own spreadsheet estimate came to $28,500 but that's only about $2,900 off, which feels like normal tracking noise for us. I calculate the run and I release it myself since we're a four-person team. Anything I should tighten up?",
      "expected_output": "An audit response that refuses to sum the two tracking sources, applies a comparability gate, treats the $2,900 gap as unexplained variance that must be resolved line by line rather than accepted, flags the missing maker-checker separation, and returns findings in the payout audit report shape.",
      "files": [],
      "expectations": [
        "Refuses to treat 3,575 as a conversion count and states the two sources cannot be summed",
        "Names the differing attribution windows (7-day vs 30-day) and differing dedup identity as the reason the two counts are not additive populations",
        "Applies a comparability gate before computing any total, covering event definition, date basis, time window, timezone, currency, counting method, and dedup identity",
        "Requires one canonical source be declared per conversion with the other reconciled against it, never a union of the two lists",
        "Treats the roughly $2,900 gap as unexplained variance that must be explained line by line rather than attributed to tracking noise",
        "Rejects 'close enough' or 'within noise' as a basis for approving the run",
        "Flags the absence of maker-checker separation: the person who calculated the run must not be the person releasing the funds",
        "States that maker-checker is the gate never dropped, and names a concrete second reader despite the four-person team size",
        "Recommends a shared idempotency key across the two trackers as the structural fix, flagged even though these duplicates were caught manually",
        "Produces a payout audit report containing Scope, Checks, Findings with severity and dollars at risk, Exceptions, Variance, Cleared, and Sign-off",
        "Shows the variance working: gross difference, the findings that explain it, and a residual that is either zero or itself a written-down finding"
      ]
    },
    {
      "id": 2,
      "prompt": "Two partners have kicked off about last quarter's commissions on our Kestrel Analytics program and I need to re-audit that run. Our written rate card says 20% on referred volume up to $10,000, 25% from $10,000 to $25,000, and 30% above $25,000, and there is no wording anywhere about how the bands combine. Partner Brightpath Media referred $26,000 of volume in the period and the platform paid her $7,800. Another partner, Onda Digital, landed at exactly $25,000 and got $6,250. We also refreshed the rate card two weeks ago. I don't have code access to any of this, the network only gives me a PDF statement. Are these numbers right?",
      "expected_output": "A recomputation showing both partners were paid on a retroactive reading, marginal recomputation of $6,050 and $5,750, the boundary-operator ambiguity at $25,000 raised as a structure finding, and a coverage method chosen for a no-code environment.",
      "files": [],
      "expectations": [
        "Recomputes Brightpath Media on the marginal (incremental) method as $6,050, showing 20% x 10,000 + 25% x 15,000 + 30% x 1,000 = 2,000 + 3,750 + 300",
        "Identifies the $7,800 paid figure as the retroactive, back-to-dollar-one reading of 30% x 26,000",
        "States the gap between the two readings for that partner as $1,750",
        "Requires the written structure to specify which method governs rather than choosing one by preference, and notes most programs use marginal to preserve margin",
        "Recomputes Onda Digital on the marginal method as $5,750",
        "Treats the exactly-at-breakpoint line as a boundary-operator question and recomputes it under both >= and > at the $25,000 threshold",
        "Raises the written structure's silence at the breakpoint as a finding against the structure's ambiguity, not as a math error",
        "Warns that retroactive tiers create cliffs and quantifies one around the $25,000 breakpoint (roughly $6,249.75 just below versus $7,500.30 just above)",
        "Requires 100% review of every line sitting near a retroactive breakpoint",
        "Recomputes against the contract terms in force at the original transaction date rather than the refreshed rate card",
        "Deletes full-population recompute from the coverage menu because no code can be executed over a PDF statement, rather than listing it as an available option",
        "Names risk-based stratified sampling as the leading coverage method in this situation and rejects random-only sampling as a rung"
      ]
    },
    {
      "id": 3,
      "prompt": "Two things landed on my desk this morning for the Wrenfield Supply affiliate program. Order 88412 was a $1,240 order at our flat 12% rate, refunded on Feb 19, but that commission locked on Feb 10 and the money went out on Feb 20, and the platform won't let me touch the record. Separately, affiliate PineRidge Deals had $3,110 of commission paid in January on orders we now know were returned. She has stopped promoting us entirely and her balance is zero. Our platform's own terms say that once commissions are paid to a partner they cannot be taken back under any circumstances. Finance has nobody free to co-sign anything this cycle. Do I just eat these?",
      "expected_output": "A remediation decision that books the post-lock refund as a forward-period negative adjustment, deletes the rungs the constraints rule out instead of ranking them lower, reports the churned partner's balance as absorbed, and names forward controls.",
      "files": [],
      "expectations": [
        "States that locked means the record cannot be reversed on-platform, not that the amount is no longer owed",
        "Books the post-lock refund as a forward-period negative adjustment carried against future earnings rather than dropping the line",
        "Establishes whether the platform carries negative partner balances at all before recommending an offset",
        "Checks whether the published terms authorize reclaiming a paid commission",
        "Checks whether the partner is still earning enough for an offset to actually land",
        "Deletes the offset rung for PineRidge Deals because she has churned out with no future earnings, rather than ranking it lower on the menu",
        "Deletes direct recovery outright, citing both the terms forbidding reclaim and the absence of a finance co-signer this cycle",
        "Reports the $3,110 as absorbed and quantified, never as recovered",
        "Presents absorb-and-document and fix-it-forward as the remaining menu, naming concrete forward controls such as a longer validation window, a reserve or holdback, a shared idempotency key, or a repriced rate card",
        "Promotes fix-it-forward to the top when post-lock refunds are routine rather than exceptional, or when the same finding class appears in two consecutive runs",
        "Computes the order 88412 commission at risk as $148.80 from the stated $1,240 order and 12% rate",
        "Ranks the remediation options by value recovered per unit of effort and relationship capital spent, not by the nominal dollar amount at stake"
      ]
    },
    {
      "id": 4,
      "prompt": "Auditing March's partner payout for Calyx Ops before release, 412 lines, $48,712.40 proposed. We pay 20% recurring revenue share capped at 12 months, and our terms give a 30-day validation window. A few things I noticed but I'm not sure any of them matter. Partner Halden Group has lines for accounts they referred back in January 2025 that are still billing monthly. One account, Meridian Freight, downgraded from the $900/month plan to the $400/month plan on Feb 10 and its commission line reads $180. Three accounts are commissioned on plan list price even though those customers are on a 20%-off annual promo. We bought out partner Sable Works' recurring stream with a $12,000 lump sum in December but I still see lines for their accounts. And two trials converted 18 days ago are already marked approved. What's the verdict?",
      "expected_output": "A per-line verdict identifying the duration-cap overpay as systematic, correcting the commission base to settled invoice amounts, voiding the bought-out partner's recurring lines, and holding the lines still inside the validation window.",
      "files": [],
      "expectations": [
        "Flags Halden Group's January-2025 accounts as past the 12-month duration cap, where month 13 onward earns zero",
        "Classifies the duration-cap breach as a systematic, compounding overpay rather than an isolated line error",
        "Identifies the $180 Meridian Freight line as 20% of the pre-downgrade $900 base and corrects it to the settled, prorated downgraded amount",
        "Requires the commission base to be the settled billing amount rather than the plan list price",
        "Corrects the three promo accounts to the discounted invoice amount actually paid by the customer",
        "Flags every remaining recurring line for Sable Works as an overpay because the buyout lump sum ended the commission stream",
        "Holds the two trial conversions from 18 days ago because the 30-day validation window has not elapsed",
        "Notes that auto-approval is a common default, so an 'approved' status is not evidence that the window elapsed or that a human reviewed the line",
        "Verifies that churned subscriptions stopped generating commission lines the cycle they lapsed",
        "Verifies proration is applied in both directions, and checks mid-cycle seat removals against the program's stated policy rather than assuming a credit",
        "Notes that B2B SaaS platforms often read the billing system directly, which collapses the reconciliation check to a spot-check, and verifies that is actually true here before collapsing it",
        "Delivers the payout audit report with per-finding severity, dollars at risk, affected lines, and a recommended action per finding"
      ]
    },
    {
      "id": 5,
      "prompt": "Running the audit on our Northbeam Outfitters network payout for the period 1 to 31 March. A couple of oddities. The export tool offered three date options, transaction date, validation date and amendment date, and I just took the default, which turned out to be validation date, then I manually moved 22 lines into March so the count would match our order database. Our network reports in UTC, our order database stamps Europe/Berlin, and my program calendar runs on America/Chicago. I set the window as 1 March 00:00 through 31 March 23:59:59. Fourteen order IDs in this run also appear in February's run because we re-exported after a late batch came in. Total proposed is $27,880. Good to go?",
      "expected_output": "A refusal to clear the run until scope is frozen: re-pull on a declared date basis rather than hand-moving lines, a single normalized timezone, a half-open window, and removal of the fourteen straddling keys.",
      "files": [],
      "expectations": [
        "Rejects hand-moving the 22 lines and requires re-pulling the export on the declared date basis instead of adjusting numbers manually",
        "Requires the date basis to be declared in run scope and held for the entire run, never mixed inside one run",
        "Recommends transaction date for reconciling to the order database, reserving validation date for what became payable this period",
        "Requires one timezone fixed in run scope with every timestamp normalized to it before the cutoff is applied",
        "Names the three-clock disagreement between network, order database, and program calendar as a cutoff risk to resolve rather than a rounding detail",
        "Replaces the closed window with a half-open window of the form period_start <= t < period_end, running 1 March 00:00 to 1 April 00:00",
        "Explains that a closed window double-counts the boundary instant",
        "Treats the 14 order IDs also present in February's run as an imminent double payment and removes them before totaling",
        "Requires diffing this run's key set against the prior run's key set as a standing check rather than a one-off fix",
        "Flags late-submitted conversions re-dated by the platform to their submission date, and any line whose event date differs from its order date, as cutoff-risk lines",
        "Requires test-mode and sandbox transactions to be excluded explicitly and verified not to have leaked into the live export",
        "Refuses to clear the run for release until run scope - period boundaries, timezone, and date basis - is frozen and unambiguous"
      ]
    },
    {
      "id": 6,
      "prompt": "Our Quill and Hearth apparel affiliate program pays 8% CPA and I have the April run ready, $52,140 proposed. Our validation window is 7 days because affiliates were complaining about waiting for their money. Customers have 30 days to return anything. Reversal rate over the last three runs was 19%, then 22%, then 24% of transaction value. There are 41 reversals in this run and roughly half of them have an empty note field. Also 63 lines in the export went from pending straight to paid, I think our platform auto-approves. Tell me what to fix.",
      "expected_output": "Findings that size the validation window to the return curve, place the reversal rate far above planning bands, require reason codes on every reversal, and hold the lines that skipped lifecycle states.",
      "files": [],
      "expectations": [
        "Flags the 7-day validation window as shorter than the 30-day customer return window and requires it be sized to the reversal curve rather than to affiliate impatience",
        "States that high-return verticals such as apparel need 30 or more days of validation",
        "Compares the 19%, 22% and 24% reversal rates against planning bands of roughly 2-8% for CPA programs and states the program runs far above band",
        "Treats the above-band reversal rate as a trigger to lengthen the validation window or tighten qualification before payout, not merely a metric to report",
        "Flags every reversal carrying no documented reason code as a finding, on the grounds that uncoded reversals are unauditable and fuel partner disputes",
        "Names usable reversal reason codes such as invalid card, return or cancellation, duplicate order, customer fraud, affiliate fraud, non-qualified lead, and test transaction",
        "Escalates any reversal coded affiliate fraud out of this audit to fraud review, verifying only the arithmetic of the reversal here",
        "Flags the 63 pending-to-paid lines as records that skipped the approved and locked states",
        "States that auto-approval means unreviewed lines approve themselves, so approved status is not evidence of review",
        "Holds every line still inside its validation window rather than releasing it with a note",
        "Tracks run-over-run KPIs including payout accuracy rate, exception value as a share of run value, reversal or clawback rate against the band, post-payout correction rate, and dispute rate"
      ]
    },
    {
      "id": 7,
      "prompt": "Verdanta Labs partner payouts, May run, 318 lines, three currencies (USD, EUR, GBP) all settled out to USD, about $44K total. Two things bug me. First, I cannot find anywhere in the platform docs which exchange rate date they use, and when I spot-checked one EUR line the platform's rate was about 1.8% off the ECB rate for that day, consistently in the same direction on the four other EUR lines I checked. Second, I ran the numbers myself and I am $3.87 off the platform overall, with basically every individual line off by a cent or two. Third, four payees are sitting at $148.22 combined, all under our $50 minimum, and last month's export shows two of them at a similar level but they are not in this month's file at all. Our materiality threshold is $250.",
      "expected_output": "A verdict treating the FX strike date as an unverified per-platform item, judging rounding drift at run level against materiality, and investigating the missing sub-threshold carry-forward.",
      "files": [],
      "expectations": [
        "Treats the FX strike date as a per-platform verification item rather than assuming one",
        "Names the three candidate strike dates: conversion date, approval date, and payout date",
        "Proposes inferring the strike date empirically by recomputing one known line at each candidate date, or asking the platform directly",
        "States that a run mixing lines converted at different strike dates fails the comparability gate",
        "Treats the consistent 1.8% spread against the reference rate as a finding, while noting it may be a disclosed platform margin to verify and document",
        "Judges the $3.87 rounding drift at run level against the $250 materiality threshold rather than per line",
        "States that per-line cent-level drift is expected and legitimate because payouts cannot split cents",
        "Requires rounding to 2 decimals at the end of the calculation rather than at each step",
        "Verifies a single rounding policy applies to every line, treating mixed truncation and rounding as evidence of two calculation paths and a root-cause finding",
        "Treats the two sub-threshold payees missing from this month's file as a carry-forward failure to investigate, requiring their balances to reappear intact",
        "States that sub-threshold balances roll forward to the next run and must never be dropped or applied as an exclusion",
        "Books the $3.87 as a below-materiality finding with its value rather than omitting it, because materiality decides whether a residual blocks release, never whether it is written down"
      ]
    },
    {
      "id": 8,
      "prompt": "Before I release the June run for Larkfield Systems ($71,300 across 96 payees) I want a check. Our payee list is 31 US individuals, 12 US LLCs, 38 EU-based publishers (mostly VAT-registered companies), and 15 elsewhere. A handful of issues: one US payee never returned a W-9 but is owed $4,800, a French publisher's form is dated 2022, and our finance lead says we should withhold nothing and sort the paperwork out after payment since these are small amounts anyway. Our network generates the publishers' invoices on their behalf. And PayPal fees are being deducted from the commission line before it even shows up in the export. Should I release?",
      "expected_output": "A payee-scoped hold for the missing and expired forms, backup withholding applied, reverse-charge and self-billing verification for the EU publishers, and a finding on fees netted into the commission line.",
      "files": [],
      "expectations": [
        "Blocks release for the payee with no W-9 on file rather than paying now and collecting the form afterwards",
        "Applies backup withholding to a US payee who failed to provide a valid W-9, and flags a run paying such a payee gross as a finding",
        "Treats the 2022-dated French publisher form as expired and therefore missing",
        "Names the correct form per payee class: W-9 for US payees, W-8BEN for foreign individuals, W-8BEN-E for foreign entities",
        "Scopes the hold to the affected payees rather than holding the entire run",
        "Rejects the pay-now-paperwork-later instruction on the grounds that releasing without valid forms is a filing problem, not only a money one",
        "Identifies cross-border EU B2B affiliate commission as a marketing service falling under the reverse charge, invoiced at 0% with the buyer self-accounting",
        "Requires the invoice to cite the reverse-charge basis and a VIES-verified VAT number",
        "Recognizes the network-generated publisher invoices as self-billing and verifies each self-billed invoice matches the audited commission total for the period",
        "Determines whether the program runs merchant-of-record or agent-of-record before judging whose ledger must carry the tax",
        "Flags the PayPal fees netted into the commission line: the audit verifies the gross commission, and downstream fees must be disclosed in the program terms rather than silently netted",
        "Treats US 1099 reporting thresholds as legislation-dependent and re-verifies them for the applicable tax year rather than stating a fixed figure as permanent"
      ]
    },
    {
      "id": 9,
      "prompt": "The payout run has to go out Thursday, it is Tuesday afternoon, and I have maybe six hours total. Ashgrove Media affiliate program, 1,930 lines, $88,400 proposed. I can export CSVs from the network but I cannot run scripts against them, I am in spreadsheets only. Our structure changed on 1 May: we moved from a flat 12% to a tiered card, and this run covers 15 April to 15 May. Nobody in finance can look at this before Thursday. Which checks do I actually run, and which do I skip?",
      "expected_output": "A triage that gives an explicit errors-per-hour ordering of the gates, promotes rate/tier recompute for the mid-period structure change, refuses to drop maker-checker, and picks a coverage method compatible with spreadsheets only.",
      "files": [],
      "expectations": [
        "Names dedup as the highest errors-caught-per-hour gate, ahead of reconciliation and comparability, lifecycle window, rate and tier recompute, and currency, tax and thresholds",
        "Distinguishes the efficiency ordering from the value ordering, in which reconciliation and comparability lead",
        "States the numbered audit sequence is a dependency order, not a priority order",
        "Refuses to drop maker-checker despite the deadline, calling it the gate never dropped, and finds a second reader outside finance rather than skipping the sign-off",
        "Promotes rate and tier recompute to the front of the order because the commission structure changed mid-period",
        "Recomputes each line against the terms in force on its own transaction date, splitting the run at 1 May rather than applying one card across the whole period",
        "Deletes full-population recompute from the coverage menu because scripts cannot be executed over the export",
        "Names risk-based stratified sampling as the leading coverage method available here",
        "Defines the stratified sample as 100% of high-value lines, high-reversal partners and top earners, plus a random sample of the remainder sized against materiality",
        "Rejects random-only sampling as a rung because it costs the same hours as risk-based and finds fewer dollars",
        "Warns that a top-earner spot-check is blind to long-tail systematics and is acceptable only under a hard deadline",
        "Recommends a reusable recompute script as the compounding fix, noting full-population recompute drops to near-zero effort on every run once the script exists"
      ]
    },
    {
      "id": 10,
      "prompt": "Hollowbrook Goods, monthly affiliate run, 2,184 lines, $31,400 proposed. Our analytics dedupe on transaction ID and 17 lines came through with the transaction ID field blank. They have all been rolled up into a single record in the report so I cannot tell what any one of them is worth. Separately, 12 orders exist in our order database with no matching record in the network, and 9 records exist in the network with nothing behind them in the order database. And one affiliate, DealVault, has 140 conversions all from the same three IP ranges inside about 40 minutes, all on our highest-value SKU. I would like to just estimate the 17 blank ones at the average line value and move on.",
      "expected_output": "A refusal to estimate quarantined lines, a two-direction exception queue with a default action per direction, stable-ID matching discipline, and escalation of the burst pattern to fraud review without ruling on it here.",
      "files": [],
      "expectations": [
        "Rejects estimating the 17 blank-key lines at an average line value",
        "Quarantines the empty or malformed dedup keys and requires manual resolution before any total is computed",
        "Explains that deduping on a blank key collapses many conversions into one record, or splits one conversion into many payable lines",
        "Requires one deterministic dedup key per conversion, paid at most once per commission event",
        "Splits the one-sided records into an exception queue carrying a direction and a default action per record",
        "Classifies the 9 network-only records as possible over-credit: hold the lines and investigate before approving",
        "Classifies the 12 order-database-only records as untracked sales, anticipates an inbound partner dispute, and requires the credit policy be decided and documented explicitly",
        "Matches records on stable identifiers only - order ID, transaction ID, customer ID, subscription ID - never on partner or customer display names",
        "Applies the zero/one/many rule: zero matches go to the exception queue, exactly one resolves, more than one stops for explicit human disambiguation rather than an auto-pick",
        "Escalates the DealVault burst pattern to fraud review without ruling on its legitimacy inside this audit",
        "Continues to verify the arithmetic of the DealVault lines and holds them, treating fraud escalation as running in parallel to a remediation action rather than replacing one"
      ]
    }
  ],
  "trigger_queries": [
    { "query": "Audit this month's affiliate payout run before we release it", "should_trigger": true },
    { "query": "Can you check our commission payout for accuracy before I hit send?", "should_trigger": true },
    { "query": "We think we overpaid two affiliates last quarter, can you re-audit that run?", "should_trigger": true },
    { "query": "I need a pre-disbursement review of our partner commission run", "should_trigger": true },
    { "query": "Our network export and our order database disagree by about $3K, what do I hold?", "should_trigger": true },
    { "query": "Reconcile the affiliate platform export against our billing system", "should_trigger": true },
    { "query": "A partner says her tier was applied wrong, I need to verify last quarter's commissions", "should_trigger": true },
    { "query": "How do I know the commission numbers the platform handed me are actually right?", "should_trigger": true },
    { "query": "Something is off in our payout file and I release it Friday", "should_trigger": true },
    { "query": "We paid the same order twice across two affiliate trackers, how do I find the rest?", "should_trigger": true },
    { "query": "Check whether our commissions were calculated on marginal or retroactive tiers", "should_trigger": true },
    { "query": "I need to verify 800 commission lines before payday", "should_trigger": true },
    { "query": "payout accuracy review for our partner program", "should_trigger": true },
    { "query": "A refund came in after the commission locked, what happens to that money?", "should_trigger": true },
    { "query": "Our clawbacks are not being applied before payout, can you review the run?", "should_trigger": true },
    { "query": "Double-check our affiliate payouts for duplicate conversions", "should_trigger": true },
    { "query": "Can you look over this commission run and tell me what to hold back?", "should_trigger": true },
    { "query": "We are about to pay $60K to affiliates and I want a sanity check first", "should_trigger": true },
    { "query": "Review our partner commission calculations for errors", "should_trigger": true },
    { "query": "Some affiliates say they were underpaid last month", "should_trigger": true },
    { "query": "How do I check that our validation window was actually respected before lines got approved?", "should_trigger": true },
    { "query": "Verify our recurring revenue share commissions against the billing system", "should_trigger": true },
    { "query": "commission reconciliation for March", "should_trigger": true },
    { "query": "Our payout total does not match my spreadsheet and I cannot find the difference", "should_trigger": true },
    { "query": "I think we have been paying commission past the 12-month cap", "should_trigger": true },
    { "query": "Does anyone need to sign off before I release affiliate payments?", "should_trigger": true },
    { "query": "Set up a review process so we stop overpaying partners every month", "should_trigger": true },
    { "query": "Audit the affiliate commission file", "should_trigger": true },
    { "query": "What checks should I run before disbursing partner commissions?", "should_trigger": true },
    { "query": "We just noticed month-13 commissions going out on a 12-month program", "should_trigger": true },
    { "query": "Our affiliate payouts include orders that were returned, how do I fix this run?", "should_trigger": true },
    { "query": "Find the errors in our partner payout before the money moves", "should_trigger": true },
    { "query": "Which lines in this commission run should I not pay yet?", "should_trigger": true },
    { "query": "Currency conversion on our affiliate payouts looks wrong to me", "should_trigger": true },
    { "query": "An affiliate was under the minimum threshold and their balance just vanished", "should_trigger": true },
    { "query": "I need a report showing what is cleared and what is held in this payout", "should_trigger": true },
    { "query": "Can you sanity check a commission run for a B2B SaaS partner program?", "should_trigger": true },
    { "query": "Our ecommerce affiliate network payout has 2,000 lines, how do I sample it?", "should_trigger": true },
    { "query": "Confirm the commission rate applied matches the contract in force at the time of the sale", "should_trigger": true },
    { "query": "affiliate payment run review", "should_trigger": true },
    { "query": "Somebody needs to check my math on this partner payout", "should_trigger": true },
    { "query": "The same conversion shows up in both January's and February's payouts", "should_trigger": true },
    { "query": "We are paying affiliates who have no tax forms on file, how bad is this?", "should_trigger": true },
    { "query": "Our finance team wants evidence that partner commissions were reviewed before release", "should_trigger": true },
    { "query": "Is this commission run safe to release?", "should_trigger": true },
    { "query": "I need to explain a $2,900 gap between our tracker and the network before payday", "should_trigger": true },
    { "query": "Recompute the commissions for our top five partners", "should_trigger": true },
    { "query": "Half our reversals have no reason recorded, does that matter for this payout?", "should_trigger": true },
    { "query": "Rounding differences across hundreds of commission lines, should I care?", "should_trigger": true },
    { "query": "How do I stop paying the same affiliate conversion twice?", "should_trigger": true },
    { "query": "Make sure our partner payouts are correct before the wire goes out", "should_trigger": true },
    { "query": "Audit a payout run for a program where the rate card changed mid-period", "should_trigger": true },
    { "query": "affiliate commission accuracy check", "should_trigger": true },
    { "query": "Design the commission tiers for our new affiliate program", "should_trigger": false },
    { "query": "What rate should we pay affiliates on a $49/month SaaS plan?", "should_trigger": false },
    { "query": "Should we use flat CPA or revenue share for our program?", "should_trigger": false },
    { "query": "How long should our cookie attribution window be?", "should_trigger": false },
    { "query": "Set the clawback and validation window policy for a program we are launching", "should_trigger": false },
    { "query": "One of our affiliates is cookie stuffing, how do I prove it?", "should_trigger": false },
    { "query": "Build the rule set to flag fraudulent affiliate traffic", "should_trigger": false },
    { "query": "Is this coupon partner actually driving incremental sales or just stealing credit?", "should_trigger": false },
    { "query": "We have a self-referral ring in the program, what do I do about it?", "should_trigger": false },
    { "query": "Draft the terms and conditions affiliates accept when they join", "should_trigger": false },
    { "query": "Write the clause that lets us terminate an affiliate for brand bidding", "should_trigger": false },
    { "query": "What should our affiliate agreement say about clawback rights?", "should_trigger": false },
    { "query": "Build a dashboard for our affiliate program metrics", "should_trigger": false },
    { "query": "What KPIs should we track for affiliate performance?", "should_trigger": false },
    { "query": "Define the formula for affiliate-attributed revenue in our BI tool", "should_trigger": false },
    { "query": "Set reward caps and cooldowns for our refer-a-friend program", "should_trigger": false },
    { "query": "Customers are gaming our referral rewards with fake accounts", "should_trigger": false },
    { "query": "How much should we give for a customer referral, account credit or cash?", "should_trigger": false },
    { "query": "Model the unit economics of our biggest reseller", "should_trigger": false },
    { "query": "Is our partner CAC better than our direct CAC?", "should_trigger": false },
    { "query": "Negotiate usage rights and rates with a creator we want to sign", "should_trigger": false },
    { "query": "Does this TikTok caption meet FTC disclosure rules?", "should_trigger": false },
    { "query": "Our affiliate blog post does not say it is sponsored, is that a problem?", "should_trigger": false },
    { "query": "Build a quarterly scorecard for our channel partners", "should_trigger": false },
    { "query": "Our gold partner missed targets two quarters running, what is the process?", "should_trigger": false },
    { "query": "Write the outreach email we use to recruit new affiliates", "should_trigger": false },
    { "query": "Where do I find affiliates for a B2B SaaS product?", "should_trigger": false },
    { "query": "Design the onboarding emails for newly approved affiliates", "should_trigger": false },
    { "query": "How many tiers should our channel program have?", "should_trigger": false },
    { "query": "Set the criteria to promote a partner from silver to gold", "should_trigger": false },
    { "query": "Write our deal registration policy", "should_trigger": false },
    { "query": "A direct rep and a partner are both claiming the same deal", "should_trigger": false },
    { "query": "Plan a co-marketing launch with our integration partner", "should_trigger": false },
    { "query": "Which cloud marketplaces should we list on?", "should_trigger": false },
    { "query": "Map our full partner ecosystem", "should_trigger": false },
    { "query": "What should we pay a channel manager?", "should_trigger": false },
    { "query": "How do I break into partnerships from customer success?", "should_trigger": false },
    { "query": "Reconcile our Stripe payouts against our bank statement", "should_trigger": false },
    { "query": "Audit our AWS bill for unused instances", "should_trigger": false },
    { "query": "Run a payroll audit before this month's pay cycle", "should_trigger": false },
    { "query": "Review our SOC 2 evidence before the auditor arrives", "should_trigger": false },
    { "query": "Check our revenue recognition treatment for annual contracts", "should_trigger": false },
    { "query": "Audit our expense reports for policy violations", "should_trigger": false },
    { "query": "Reconcile the general ledger to the subledger for Q1", "should_trigger": false },
    { "query": "Our sales reps' commission plan payouts look wrong this quarter", "should_trigger": false },
    { "query": "Set quotas and accelerators for the AE comp plan", "should_trigger": false },
    { "query": "Audit our vendor invoices for duplicate payments", "should_trigger": false },
    { "query": "Review our Google Ads spend for wasted budget", "should_trigger": false },
    { "query": "Check our influencer campaign results against the media plan", "should_trigger": false },
    { "query": "Build the measurement plan before we launch the creator campaign", "should_trigger": false },
    { "query": "Draft a creative brief for the creator we just signed", "should_trigger": false },
    { "query": "Shortlist creators for our spring campaign", "should_trigger": false },
    { "query": "Which partner categories should we add next year?", "should_trigger": false }
  ]
}
references/audit-report-examples.md›
# Audit Report Examples

Two worked reports - one B2B SaaS recurring revenue-share, one B2C ecommerce order-level CPA - and one negative example. Program names and figures are illustrative; the structure and the reasoning per finding are the load-bearing parts.

## Worked example 1 - B2B SaaS recurring revenue-share

```
PAYOUT AUDIT - NimbusStack partner program, run 2026-03A, period 2026-02-01..2026-03-01 (UTC, billing-event date)
Scope     : 412 lines, proposed total $48,712.40 USD; sources: platform commission export vs billing-system settled invoices
Structure : 20% recurring rev-share, 12-month cap, marginal tier to 25% above $5,000 referred MRR/month
Checks    : 1 scope frozen: pass | 2 exports pulled: pass | 3 comparability: pass (same event = settled invoice, same window, UTC, USD)
            4 totals: FAIL ($496.70 over) | 5 exceptions: 2 one-sided | 6 dedup: pass | 7 rate/tier: FAIL (2 findings)
            8 lifecycle: FAIL (9 lines in window) | 9 currency/tax: FAIL (1 payee) | 10 sampling: 100% top-5 partners + 40 random
Findings  :
  F-1 | blocker       | $1,912.00 | 9 lines   | Referred trials converted <30 days ago - validation window not elapsed. HOLD lines to run 2026-04A.
  F-2 | material      |   $312.50 | 1 partner | Tier applied retroactively to full volume; structure specifies marginal. CORRECT and re-run.
  F-3 | material      |   $148.20 | 3 lines   | Commission on pre-downgrade MRR; invoices settled at downgraded amount. CORRECT to settled base.
  F-4 | minor         |    $36.00 | 2 lines   | Commission on list price; customers paid discounted invoices. CORRECT to discounted base.
  F-5 | blocker (payee-scoped) | $841.10 | 1 payee | W-8BEN expired 2026-01-31. HOLD this payee's payout until re-collected.
Exceptions: 1 in-platform-not-in-billing ($97, invoice voided post-tracking) - held, over-credit.
            1 in-billing-not-in-platform ($203 invoice, no tracked referral) - untracked; expect partner dispute; credit policy: manual review.
Variance  : recomputed $48,215.70 vs proposed $48,712.40 = $496.70; F-2 $312.50 + F-3 $148.20 + F-4 $36.00 = $496.70. Unexplained $0.00. PASS.
Cleared   : 402 lines, $45,462.60 at corrected values (excludes F-1 held lines and F-5 payee).
Sign-off  : preparer J. Ortiz (partner ops) | checker M. Lund (finance) - roles differ | 2026-03-04 | RELEASE cleared lines only.
```

Why it passes: every line is reconciled or on the exception list, the three corrections account for the gross variance to the cent, and both blockers are resolved by holding - not by releasing with a note.

Note the variance line shows its working: gross difference, the findings that explain it, residual. A residual is either zero or it is a finding with its own line - report 2's F-4 books $3.87 of rounding drift exactly that way. "Unexplained $X, below materiality, PASS" is the habit the negative example below exists to condemn; materiality decides whether a residual blocks release, never whether it gets written down.

## Worked example 2 - B2C ecommerce order-level CPA

```
PAYOUT AUDIT - Alder & Vine affiliate program, run 2026-03B, period 2026-01-01..2026-02-01 (UTC, transaction date; 45-day validation window elapsed)
Scope     : 2,184 lines, proposed total $31,406.18 USD; sources: network transaction export vs order database
Structure : flat 10% of net order value, new-customer only, 45-day validation sized to the apparel return curve
Checks    : 1 pass | 2 pass | 3 comparability: FAIL then normalized (network export was validation-date basis; re-pulled on transaction date)
            4 totals: FAIL ($2,252.14 over) | 5 exceptions: 12 one-sided | 6 dedup: FAIL (2 findings) | 7 rate: pass on sample
            8 lifecycle: FAIL (reversals missing) | 9 currency/tax: pass | 10 sampling: 100% of lines >$100 + top-3 affiliates + 60 random
Findings  :
  F-1 | blocker  | $1,742.30 | 61 order IDs | Same orders in network export AND in-house tracker, both summed into the proposed total.
                                              COLLAPSE to network as canonical source, re-run totals.
  F-2 | material |   $509.84 | 38 lines     | Orders refunded inside the 45-day window but reversals never applied - data came from
                                              a pre-validation export. APPLY reversals.
  F-3 | material | unknown   | 17 lines     | Empty transaction IDs collapsed into one analytics record; per-line value unverifiable.
                                              QUARANTINE; resolve keys manually before release.
  F-4 | minor    |     $3.87 | run-wide     | Per-step rounding drift. RECOMPUTE rounding at the end; below materiality, fix forward.
  F-5 | informational | $148.22 | 4 payees  | Below $50 threshold - verify carry-forward to next run; not payable now, must not vanish.
Exceptions: 12 in-orders-not-in-network (gift-card orders, non-commissionable per terms) - documented, no credit.
Variance  : recomputed $29,154.04 vs proposed $31,406.18; after F-1/F-2 corrections, unexplained $0.00. PASS pending F-3.
Cleared   : 2,106 lines, $28,896.71 at corrected values (excludes F-3 quarantine and F-5 carry-forward).
Sign-off  : preparer R. Okafor (affiliate mgr) | checker T. Devine (controller) | 2026-03-16 | RELEASE cleared lines; F-3 to 2026-04B.
```

Why it passes: the comparability failure was fixed by re-pulling on the declared date basis instead of adjusting numbers by hand, the double-count was collapsed to one canonical source, and the unverifiable quarantine lines were held rather than estimated.

## Negative example - what a bad audit looks like

```
PAYOUT REVIEW - Alder & Vine, March run
Network says 1,904 conversions (7-day window), our tracker says 1,671 (30-day window) -> total 3,575 conversions, looks healthy.
Proposed payout $31,406.18; our own estimate came to $28,500 - difference is only ~$2,900, close enough given tracking noise. APPROVED.
Order #88412 was refunded on Feb 19 but the commission already locked Feb 10, so nothing to do - leaving the line as paid.
Sign-off: affiliate manager (calculated and released).
```

Why every line of it fails:

- **Summing incomparable sources**: the two counts have different attribution windows and dedup identities - they are two claims about overlapping conversions, not additive populations. The comparability gate exists precisely to stop this total from being computed.
- **"Close enough" variance**: $2,900 of unexplained variance is a silent gap, far above any sane materiality threshold, and each dollar in it is either an overpay or an underpay to a real partner. Unexplained variance must be driven to or below materiality line by line, or the run holds.
- **Ignoring the post-lock refund**: locked means "cannot reverse on-platform", not "no longer owed". The refund becomes a forward-period negative adjustment carried against the affiliate's future earnings; dropping it converts a recoverable correction into a permanent overpay.
- **No maker-checker**: the same person calculated and released. There is no independent gate to catch any of the above - which is exactly how all of the above shipped.
references/currency-tax-and-payment.md›
# Currency, Tax, and Payment

The checks that turn a correct commission calculation into a correct amount of money in the right hands.

## FX strike date

- A cross-currency commission can be converted at the conversion date, the approval date, or the payout date - three different amounts.
- The strike date is under-documented on most platforms; treat it as a per-platform verification item, never an assumption. Ask the platform, or infer it by recomputing one known line at each candidate date.
- Record the verified strike-date policy in the run scope; a run mixing lines converted at different strike dates fails the comparability gate.
- Diff the rate the platform actually applied against an independent reference rate for the strike date; a systematic spread is a finding (it may be a disclosed platform margin - verify, then document).

## Rounding

- Round to 2 decimals, at the end of the calculation, not at each step - per-step rounding compounds across hundreds of small lines.
- Per-line cent-level drift is expected and documented in the industry (a 7.50% rate can legitimately show as 7.49% or 7.51% on a single line because payouts can't split cents). Judge drift at run level against materiality, not per line.
- Verify one rounding policy applies to every line; mixed policies (some lines truncated, some rounded) indicate two calculation paths and deserve a root-cause finding.

## Minimum thresholds and carry-forward

- Sub-threshold balances roll forward to the next run - they must never be dropped. Verify last run's sub-threshold payees reappear with their balance intact.
- Real network minimums are small (examples on record: $20 network minimum, $5 platform minimum); program-side thresholds of $50-100 are common. Use whatever the program's terms state.
- A payee sitting under threshold for many consecutive runs is a dormancy signal worth surfacing, not an error.

## Payment-method fees

- Establish who bears each fee - platform, program, or partner - per payment rail (PayPal, wire, ACH/SEPA, payout providers). Fee-bearing varies by platform and rail; one documented example charges partners a per-withdrawal fee and vendors a 3.5% credit-card fee while ACH is free.
- The audit verifies the _gross_ commission; fees deducted downstream must be disclosed in the program terms, not silently netted into the commission line.
- Payment rails are deliberately not ranked here. The auditor verifies who bears each fee and does not pick the rail, and a program's rail is set by payee geography and payout size - any ordering would be false precision.

## Tax-form prerequisites

- Collect before paying, not after: **W-9** for US payees, **W-8BEN** for foreign individuals, **W-8BEN-E** for foreign entities. Platforms commonly block payout release until forms are on file - the audit blocks the same way.
- Backup withholding applies to US payees who fail to provide a valid W-9; a run paying such a payee gross is a finding.
- Expired or mismatched forms count as missing.

## US reporting thresholds (dated - re-verify before relying on them)

Current as of the 2025 US law change (OBBBA, signed July 2025); thresholds shift with legislation and inflation indexing, so re-verify each tax year:

- **1099-NEC / 1099-MISC**: $2,000 for payments made after December 31, 2025 (up from $600), inflation-indexed from 2027; royalties remain at $10.
- **1099-K** (third-party settlement organizations): reverted to $20,000 AND more than 200 transactions; the planned $600/$2,500 phase-down was repealed.
- Threshold changes alter which payees need forms and filings - re-verify W-9/W-8 coverage whenever the thresholds move.

## EU VAT and self-billing

- Affiliate commission is a marketing service. Cross-border B2B within the EU uses the **reverse charge**: the supplier invoices at 0% and the buyer self-accounts; the invoice must cite the reverse-charge basis (e.g. Article 196, Directive 2006/112/EC) and a VIES-verified VAT number.
- Major networks operate **self-billing**: the network generates the publisher's invoice on the publisher's behalf. The audit verifies the self-billed invoice matches the audited commission total for the period - it is the partner-facing artifact of the run.
- **Merchant-of-record vs agent-of-record** decides who carries the tax burden: an MoR network handles collection/self-billing; under an agent model the advertiser self-assesses VAT/GST under reverse charge and invoices the publisher supply directly. Confirm which model the program runs before judging whose ledger must show the tax.
references/lifecycle-and-clawback.md›
# Lifecycle and Clawback

Every commission record moves through the same generic state machine, whatever the platform calls the states:

```
pending -> approved/validated -> locked -> paid
                 |                  |
                 v                  v
             reversed        (post-lock: forward-period
          (in-window)         negative adjustment)
```

- **Pending**: tracked, not yet payable; fully reversible.
- **Approved/validated**: passed review or the validation window elapsed; on most platforms still reversible until lock.
- **Locked**: no further edits or reversals on-platform; queued for payment.
- **Paid**: money moved; corrections only via negative balance or off-platform recovery.

## How real platforms name and time the gates

Platform documentation; verify against the live program before relying on a number.

| Platform                               | Reversal buffer         | Lock/payment timing                                                                 | After lock/payment                                                                                                                                 |
| -------------------------------------- | ----------------------- | ----------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------- |
| impact.com                             | "action locking period" | Default: locks ~27th of the following month, pays ~20th of the month after locking  | "You cannot modify or reverse actions after they lock" - remedy is a make-good transfer                                                            |
| CJ Affiliate                           | "locking cycle"         | Prior-month transactions lock the 10th; custom cycles 7-60 days from post date      | Locked transactions "can no longer be edited or voided"; corrections via full-reversal-plus-new-record restatement                                 |
| Awin                                   | "validation period"     | Auto-validation up to 67 days on the entry tier; average approval 30-45 days        | On the entry tier, "Approved transactions cannot be reversed"; amendments require pending status                                                   |
| PartnerStack                           | 7-day invoice review    | Commissions withdrawable the following month                                        | "Once you have paid out commissions to a partner, under no circumstances can they be taken back"                                                   |
| Rewardful / FirstPromoter / Tapfiliate | "due period" / net-30   | Pending becomes due after ~30 days by default (configurable to the refund policy)   | Refunds auto-apply as negative commissions; a disapproved paid commission drives the balance negative (e.g. -$10) and nets against future earnings |
| Amazon Associates                      | ~30-day return window   | Commission disqualified when "a cancellation, return, or refund has been initiated" | Reserves the right to offset "any excess payment" against subsequent commission income                                                             |

## What releases each transition

- Pending → approved: expiry of the validation window (auto-approval is the common default - unreviewed lines approve themselves) or explicit manual approval.
- Approved → locked: the platform's locking date or cycle.
- Locked → paid: the payment schedule, minimum threshold, and tax-form status.
- The audit checks that no line skipped a state (paid without approved, approved while still inside its window) and that the window length actually matches the program's refund/return policy.

## Reversal triggers by model

- **B2C order-level CPA**: return, chargeback, cancellation, partial refund (amend the sale amount and recompute at the original transaction's rate), non-qualifying coupon.
- **B2B SaaS revenue-share**: trial-to-paid failure, refund, downgrade (reduce the base), mid-period or annual-contract churn, failed payment.
- Reversal rates are the ceiling on payout accuracy. Planning bands from vendor benchmarks (not audited data): 2-8% for CPA, under 3% for revenue-share; some verticals run far hotter - hosting programs have been reported reversing 50-80% of transactions. Above band → lengthen the validation window or tighten qualification before payout.
- Size the window to the reversal curve, not the calendar: high-return verticals (apparel returns run roughly 25%+) need 30+ days; SaaS needs refund policy plus the trial-to-paid window. Geno Prussakov's documented reversal SOP: locking periods sized to the return/cancellation window - "affiliates don't get any certainty on their earnings until that commission locks."

## The post-lock refund problem

- A refund arriving after lock (or after payment) cannot be reversed on-platform on most systems - which is what moves the line out of the correction path and into the remediation ranking in `SKILL.md`.
- Establish three facts before recommending a rung there:
  - Whether the platform carries negative partner balances at all - offset is unavailable where it does not.
  - Whether the published terms authorize reclaiming a paid commission - several platforms in the table above state flatly that they do not.
  - Whether the partner is still earning enough for an offset to land.
- A partner who churns out with a negative balance is an unrecoverable loss - report it as absorbed, never as recovered, and quantify it.
- Reserves/holdbacks (retaining a slice of each payout against future reversals) are the usual form the fix-it-forward rung takes here; note their absence as a program-level observation when post-lock refunds recur.

## Reason codes

- Require a documented reason on every reversal. Prussakov's SOP lists these reason codes, and networks use similar correction-reason lists:
  - invalid card
  - return/cancellation
  - duplicate order
  - customer fraud
  - affiliate fraud
  - non-qualified lead
  - test transaction
- Uncoded reversals are unauditable and fuel partner disputes; flag any reversal without a reason as a finding.
- A reversal coded "affiliate fraud" leaves this skill's scope - escalate to fraud review; the audit only verifies the arithmetic of the reversal.
references/rate-and-tier-verification.md›
# Rate and Tier Verification

Recompute every line's expected commission independently from raw inputs, then diff against the proposed value. Never trust the platform's own stated total as evidence of its own correctness.

## Marginal vs retroactive tiers - worked math

Example structure: 20% up to $10,000 referred volume, 25% from $10,000 to $25,000, 30% above $25,000. Partner's period volume: $26,000.

- **Marginal (incremental)** - each band pays its own rate:
  `20% x 10,000 + 25% x 15,000 + 30% x 1,000 = 2,000 + 3,750 + 300 = $6,050`
- **Retroactive ("back to dollar one")** - crossing a threshold re-rates the entire volume:
  `30% x 26,000 = $7,800`

Same partner, same sales, $1,750 apart. The audit must know which method the written structure specifies and recompute with exactly that method - most programs use marginal to preserve margin.

Retroactive tiers create cliffs: at $24,999 the retroactive payout is `25% x 24,999 = $6,249.75`; at $25,001 it is `30% x 25,001 = $7,500.30`. Two dollars of revenue moved $1,250.55 of commission. Treat every line near a retroactive breakpoint as high-risk and review it 100%.

## The boundary-operator bug

- Decide whether the threshold dollar itself earns the old rate or the new one: `>=` vs `>` at each breakpoint.
- Recompute boundary lines under both operators; if the results differ and the written structure is silent, raise a finding - the structure is ambiguous, not the math.
- Walk marginal tiers in ascending order, consuming each band's capacity (`up_to - from`) before spilling into the next; a common implementation bug applies the top rate flat instead of graduating.

## Overrides checklist - verify per line

- **Partner-type override**: agency, creator, reseller, coupon/cashback rates differ; confirm each line used its partner's assigned rate card, not the program default.
- **Promo/bonus windows**: evaluate against start AND end datetimes in the run's timezone; a promo rate applied outside its window is a finding either way.
- **New-customer gating**: confirm how "new" is determined - email, customer ID, first-order flag - and that the gate was actually evaluated, not assumed.
- **Sub-affiliate / second-tier overrides**: a parent's override commission needs its own line, its own rate, and its own dedup key - and must never double-pay the child's base commission.
- **Grandfathered legacy rates**: partners kept on an old rate card after a migration; verify against the partner's contract, not the current published card.
- **Buyout clauses**: a recurring-commission partner may have been bought out (lump sum ends the stream); any recurring line for a bought-out account is an overpay.

## Terms in force at the transaction

Recompute against the contract terms active on the original transaction date - never today's terms. At least one major platform re-evaluates a _modified_ action against currently-active contract terms, so an expired promo can silently change a historical line's payout when the line is edited. Any line whose applied rate matches today's card but not the transaction-date card is a finding.

## Recurring revenue-share specifics (B2B SaaS)

- **Commission base**: MRR vs ARR - confirm which, and confirm the base is the _settled_ billing amount, not the plan list price.
- **Discounts and coupons** reduce the commissionable base; a line commissioned on list price while the customer paid a discounted invoice is an overpay.
- **Proration**: mid-cycle upgrades/downgrades produce prorated invoices; the commission must follow the prorated amount, both directions.
- **Seat expansion**: added seats mid-cycle are commissionable on the prorated add; verify removed seats follow the program's stated policy (many programs give no mid-cycle credit for removals - check the policy, then check the data matches it).
- **Duration cap**: a 12-month cap means month 13's billing event earns zero; lifetime means it keeps paying. Verify each recurring line's month index against the cap - month-13 payments under a 12-month cap are a systematic, compounding overpay.
- **Churn**: a churned subscription must stop generating lines the cycle it lapses; pay in arrears on confirmed billing events so this happens automatically, and flag any commission line without a matching settled invoice.
references/reconciliation-and-dedup.md›
# Reconciliation and Dedup

How to pull comparable data, match it across systems, and pay each conversion exactly once.

## Export pull and date basis

- Pull the platform/network conversion export and the source-of-truth extract for the same declared period, before touching any number.
- Exports commonly offer more than one date basis - transaction date, validation date, amendment date (Awin exposes all three). Picking the wrong one silently shifts lines between runs.
- Default to transaction date for reconciling to orders/billing; use validation date when reconciling what became payable this period. Never mix bases inside one run.
- Late-submitted conversions may be re-dated by the platform to their submission date (impact.com documents this), putting them on a new locking/payment schedule. Treat any line whose event date differs from its order date as a cutoff-risk line.
- Paginate fully on both sides. A partial page reads as "missing in the other system" and produces false exceptions.
- Exclude test-mode and sandbox transactions explicitly; verify test data cannot leak into the live export before comparing counts.

## Matching keys

- Match on stable identifiers only: order ID, transaction ID, customer ID, subscription ID. Never match on partner or customer display names.
- Apply the 0/1/many rule: zero matches → exception queue; exactly one → resolved; more than one → stop and require explicit human disambiguation, never auto-pick.
- Declare the join key between platform record and internal record before matching - and keep it for every future run, so exceptions are comparable run over run.

## The comparability gate

Before comparing or summing any two numeric sources, confirm they share every one of:

- Event definition (order placed vs order paid vs subscription invoice settled).
- Date basis and time window.
- Timezone.
- Currency.
- Counting method (per order vs per line item vs per customer).
- Deduplication identity (which key collapses duplicates, and whether blanks collapse together).

If any dimension differs: normalize first, or present the numbers side by side with their definitions and refuse to compute a total. Two sources claiming the same conversion are one conversion with two claimants - never sum across claimant sources; de-dupe against the declared source of truth.

Expect and budget for a residual gap: report claimed vs verified vs delta with an explanation, rather than forcing a false reconciliation to zero.

## The exception queue

Every one-sided record goes into a queue with a direction and a default action:

| Direction                          | Meaning                                                               | Default action                                                             |
| ---------------------------------- | --------------------------------------------------------------------- | -------------------------------------------------------------------------- |
| In platform, not in billing/orders | Possible over-credit - a commission with no confirmed money behind it | Hold the line; investigate before approving                                |
| In billing/orders, not in platform | Untracked sale - the partner likely earned and wasn't credited        | Expect an inbound dispute; decide credit policy explicitly and document it |

- Age each open exception in days; escalate by amount and by age; a growing exception count run over run is a process defect, not noise.
- Close the run only when every exception is resolved or explicitly accepted and listed in the report.

## Period cutoff and timezone

- Use half-open windows: `period_start <= t < period_end`. Closed windows double-count the boundary instant.
- Normalize every timestamp to the single timezone declared in run scope before applying the cutoff. Platform, billing system, and program office frequently disagree on the clock.
- Diff this run's key set against the prior run's key set. Any overlap is a conversion about to be paid twice - remove it before totaling.
- Watch conversion lag: click date, conversion date, and platform post date can span a boundary; the declared date basis decides which run owns the line.

## Dedup key discipline

- One deterministic dedup key per conversion; pay each key at most once per commission event.
- Quarantine empty or malformed keys instead of letting them collide: analytics tools that dedupe on transaction ID treat all blanks as one record, collapsing many conversions into one (a documented GA4 behavior) - or one conversion into many payable lines elsewhere.
- Where the platform holds multiple records for one order ID, identify which record the platform treats as payable (impact.com applies updates to the newest action for a shared order ID, and its reattribution flow leaves only the new pending action payable). Verify exactly one payable record survives per key.

## Dual tracking collapse

- If both a platform pixel and an in-house tracker (or web analytics) record conversions, declare one canonical source per conversion type and reconcile the other against it - never union the two lists.
- The fix for persistent dual-tracking duplicates is structural: a shared idempotency key across both trackers. Flag its absence as a finding even when this run's duplicates were caught manually.
SKILL.md›
---
name: affiliate-payout-audit
description: Audit an affiliate or partner commission payout run for accuracy before money is disbursed - per-line rate and tier recomputation, double-counted conversions, clawback and validation windows, period cutoff, currency, rounding, and tax-form status, delivered as an audit report with severities and recommended holds. Covers B2B SaaS recurring revenue-share and B2C ecommerce order-level CPA programs. Use whenever the user mentions an affiliate payout audit, commission accuracy, payout reconciliation, overpaid or underpaid affiliates, or a pre-disbursement review, even if they never say audit. Do NOT use for spotting fraudulent affiliates - use mbfinotti/partnerships-skills@affiliate-fraud-detection instead.
license: MIT
metadata:
  author: Maya-Beth Finotti
  version: "1.1.10"
---

# Affiliate Payout Audit

Audit one affiliate/partner commission payout run before release: recompute the math, verify the rules, and report findings with recommended holds and corrections. Assume the activity already passed fraud screening - this skill checks whether the payout is _correct_, not whether it is _legitimate_.

No named, affiliate-native reconciliation framework exists; platforms ship locking and dispute mechanics, not a methodology. The method here borrows openly from adjacent finance disciplines:

- **Account reconciliation** (preparer/reviewer).
- **Three-way match** - platform export vs billing/order system vs CRM, the affiliate analogue of PO/receipt/invoice.
- **Revenue assurance**.
- **Segregation of duties / four-eyes (maker-checker)**.
- **SOX ICFR / COSO** - where the program sits inside a public company.

Say this plainly to the user; never present a branded framework.

## Interview

Ask before auditing anything. One question per message; offer multiple-choice answers when possible. Skip anything the user already answered.

- B2B SaaS recurring revenue-share, B2C ecommerce order-level CPA, or both?
- Commission structure in force: rate model, tier method (marginal or retroactive), per-partner-type overrides, promo/bonus windows, new-customer gating, sub-affiliate overrides, grandfathered legacy rates?
- Payout cadence, and this run's exact period boundaries - dates and timezone?
- Which data exports exist, and what is the internal source of truth: billing system, order database, or CRM closed-won?
- Validation/locking window length? What event releases a commission from pending to approved?
- Is any line in this run already paid, or is everything still reversible?
- Minimum payout threshold, currencies involved, FX handling policy?
- Tax-form status per payee - collected, missing, expired?
- Known historical error types and past partner disputes?
- Who calculates the run, and who releases the funds? Same person or different?
- Materiality threshold? Is the program subject to external financial-controls requirements (public-company ICFR)?
- By what date must the audit result land, and is the release date itself movable?
- One-off or compounding: fix this run only, or leave standing controls behind - a reusable recompute script, a reserve policy, a longer validation window?
- Effort ceiling: review hours available, whether you can execute code over the exports, whether finance can co-sign, and how much partner-relationship capital this run can spend?

Missing period boundaries, source of truth, or commission structure block the audit - insist before recomputing anything.

The last three answers re-rank every menu below:

- A hard release date promotes the cheap gates and drops full-population recompute.
- A compounding mandate promotes both full-population recompute and the fix-it-forward rung.
- No code access deletes full-population recompute outright.
- No finance co-signer deletes direct recovery from this run.

## The Audit Sequence

Run the checks in order; each gate assumes the previous one passed. The underlying lifecycle is the same five-gate machine everywhere: pull/export → reconcile to source of truth → verify rate/tier per line → dedupe → lock → pay.

**When the release date won't fit all twelve.** The numbering is a dependency order, not a priority order. To spend a short budget well, rank the gates by errors caught per hour:

- efficiency: dedup (6) > reconcile and comparability (3-4) > lifecycle window (8) > rate/tier recompute (7) > currency, tax, thresholds (9)
- value: reconcile and comparability (3-4) > dedup (6) > rate/tier recompute (7) > lifecycle window (8) > currency, tax, thresholds (9)
- effort: rate/tier recompute (7) - an hour with code, a week by hand > reconcile (3-4) - an hour on clean exports, a week on messy ones > lifecycle window (8) == currency, tax, thresholds (9) - an hour each, date arithmetic and a checklist > dedup (6) - near-zero with code over the key set
- compliance cost: currency, tax, thresholds (9) - a payee paid without a valid tax form is a filing problem, not just a money one > maker-checker (12) - the sign-off is the evidence an external control test asks for > every other gate == none, because being wrong there costs money, not a filing

Maker-checker (12) is never the gate you drop: it costs one scheduling and a second reader, and its absence is what let every error in the negative example ship.

Rate/tier recompute is what this order starves - highest value of the arithmetic gates, most expensive by hand. Promote it to first when:

- a partner has disputed a tier
- the run straddles a retroactive breakpoint
- the structure changed mid-period

The order is a default, not a law, and it shifts with who executes it: a network exporting full machine-readable logs collapses (6) and (7) to near-zero and moves both up; no billing-system access puts (3-4) out of reach and makes (7) the whole audit.

1. **Freeze the run scope.** Record run ID, period start/end, timezone, date basis (transaction vs validation vs amendment date), currencies, materiality threshold, and every data source. Ambiguous scope → stop and resolve; every later check depends on it.
2. **Pull the exports on the declared date basis.** Platform/network conversion export plus the internal source-of-truth extract for the same period. If you can analyze an uploaded spreadsheet or CSV, load both; otherwise ask the user for line counts, totals, and specific lines to read out.
3. **Comparability gate.** Before comparing or summing any two numeric sources, confirm they share the same event definition, date basis, time window, timezone, currency, counting method, and dedup identity. Any mismatch → normalize first, or report the numbers side by side with their definitions and refuse to compute a total. Detail in [references/reconciliation-and-dedup.md](references/reconciliation-and-dedup.md).
4. **Reconcile totals to the source of truth.** Compare line count and money value: platform export vs billing/orders/CRM. Pass = totals match within materiality. Fail = a gap that must be explained line by line before anything is approved - never "close enough".
5. **Work the exception queue.** Records in one system but not the other, matched on stable IDs only.
   - In platform, not in billing: possible over-credit, hold and investigate.
   - In billing, not in platform: untracked sale, expect an inbound partner dispute and decide credit policy explicitly.
6. **Dedupe.** One deterministic key per conversion (order ID / transaction ID / subscription ID). Quarantine empty or malformed keys. Collapse dual tracking (platform pixel + in-house tracker) to one canonical source. Check no key appears in the prior run - period straddle pays twice.
7. **Verify rate and tier per line.** Recompute expected commission from raw inputs using the terms in force at the original transaction date: tier method and boundary operator, partner-type overrides, promo windows against start/end datetimes, new-customer gating, sub-affiliate overrides, grandfathered rates. If you can execute code, recompute every line; otherwise recompute the sampled lines by hand. Math in [references/rate-and-tier-verification.md](references/rate-and-tier-verification.md).
8. **Verify lifecycle state per line.** Every line must have cleared its validation/locking window or been explicitly released; reversals for in-window refunds/churn must already be applied; refunds that landed after lock must appear as forward-period negative adjustments, not vanish. Rules in [references/lifecycle-and-clawback.md](references/lifecycle-and-clawback.md).
9. **Verify currency, rounding, tax, and thresholds.** FX strike date matches policy, rounding to 2 decimals at the end, sub-threshold balances carried forward, tax forms on file for every payee in the run. Detail in [references/currency-tax-and-payment.md](references/currency-tax-and-payment.md).
10. **Choose the coverage method**, ranked by errors found per hour:
    - efficiency: full-population recompute > risk-based stratified > top-earner spot-check
    - value: full-population recompute > risk-based stratified > top-earner spot-check
    - effort: risk-based stratified - an hour to design, then a week by hand at volume > top-earner spot-check - an hour > full-population recompute - an hour to write the script, near-zero every run after
    - Effort inverts value here: the most thorough method is also the cheapest once the script exists, and it is the only rung that compounds. It leads only when you can execute code over a complete, machine-readable export; without that it is off the menu entirely, and risk-based stratified leads.
    - Risk-based stratified = 100% of high-value lines, high-reversal partners, and top earners (a small share of partners drives most commission dollars), plus a random sample of the remainder sized against materiality. Top-earner spot-check catches concentration errors and is blind to long-tail systematics - take it only under a hard deadline.
    - Random-only sampling is not a rung: it costs the same hours as risk-based and finds fewer dollars. Use randomness for the remainder, never for the whole run. Document the method and rationale per selection.
11. **Compile the report.** Classify every line cleared / held / corrected; every discrepancy becomes a finding with severity, value at risk, and a recommended action (see Output, then Remediation). Escalate any pattern that smells illegitimate - self-referral, coordinated bursts - to fraud review; never rule on it here.
12. **Maker-checker sign-off.** The person who calculated the run must not be the person releasing funds; the audit report is the checker's evidence. Zero blocker findings outstanding before release - otherwise hold the affected lines or the run.

If your harness has persistent memory, memorize the run's parameters and accepted exceptions (period rules, source of truth, materiality, known one-sided records) so the next run starts from them; otherwise write them into the report header for reuse.

## Output: the Audit Report

Deliver every audit as this artifact:

```
PAYOUT AUDIT - <program>, run <id>, period <start>..<end> (<timezone>, <date basis>)
Scope     : <N> lines, proposed total <amount> <currency>; sources: <platform export> vs <source of truth>
Checks    : each numbered check above -> pass / fail / n-a, one line each
Findings  : F-<n> | severity: blocker / material / minor / informational | $ at risk | affected lines |
            action: correct before release / hold line / offset against future commission / absorb and
            document / fix it forward / direct recovery (ranked below) + escalate to fraud review if warranted
Exceptions: one-sided records with their queue (over-credit vs untracked) and resolution
Variance  : recomputed total vs proposed total; unexplained net variance vs materiality threshold
Cleared   : lines and value cleared for release (at corrected values)
Sign-off  : preparer, checker (must differ), date, release decision
```

Worked B2B SaaS and B2C ecommerce reports, plus a negative example, in [references/audit-report-examples.md](references/audit-report-examples.md).

## Remediation: the Action on Each Finding

Finding the error is the cheap half. Rank the response by value recovered per unit of effort and partner-relationship capital spent - never by how much money is nominally at stake.

1. **Correct before release.** Recompute the line, fix the run, pay the right number. Full recovery, no relationship cost - the partner never saw the wrong figure. Default whenever the line is still unpaid.
2. **Offset against future commission.** Book the correction as a forward-period negative adjustment carried against the partner's next earnings. Default once the line is paid. Full recovery, but only while the partner keeps earning.
3. **Absorb and document.** Write the line off and record it as a finding with its value. The honest rung below materiality, and the only one left for a partner who has churned out with a negative balance.
4. **Fix it forward.** Lengthen the validation window, add a reserve/holdback, install the shared idempotency key, or renegotiate that partner's rate card when the discrepancy is structural mispricing rather than an error. Recovers nothing from this run; retires the whole error class.
5. **Direct recovery.** Invoice the partner for the overpayment. Uncertain recovery, and the only rung that can end the relationship.

- efficiency: correct before release > offset > absorb and document > fix it forward > direct recovery
- value recovered: correct before release == offset > direct recovery > absorb and document == fix it forward
- effort: direct recovery - a week to a quarter of demand, finance and legal coordination > fix it forward - a week to build, then a standing job > offset - an hour, near-zero where the platform carries negative balances > correct before release == absorb - near-zero, a single edit each
- relationship capital: direct recovery > fix it forward > offset > correct before release == absorb - nothing spent, the partner is never asked for anything
- compliance cost: direct recovery - contract interpretation and legal sign-off, irreversible once sent > offset - must be authorized by the published terms, and several platforms state flatly that a paid commission can never be reclaimed > fix it forward - a terms amendment with notice to partners > correct before release == absorb - none

Justifying the ties:

- Correct-before-release and offset: both recover 100% of the line, differing only in timing.
- Absorb and fix-it-forward: both recover nothing from this run, differing only in what they buy later.
- Correct-before-release and absorb: each one edit that asks the partner for nothing.

- Delete, don't demote. If nothing in this run is paid, rungs 2, 3 and 5 are not alternatives - drop them and correct. If the terms or the platform forbid reclaiming a paid commission, delete offset and direct recovery; absorb and fix-it-forward are the whole menu.
- Fix it forward is what this order starves: high value, zero recovery this run, so it loses every round. Promote it to the top the moment the same finding class appears in two consecutive runs, or when post-lock refunds are routine rather than exceptional.
- Escalation to fraud review is not a rung. It answers a different question and runs in parallel with whichever rung is chosen.
- The order is a default, not a law, and it shifts with who executes it. Re-rank against what you already know:
  - A platform carrying negative balances natively promotes offset.
  - No finance co-signer this cycle deletes direct recovery from this run.
  - A roster small enough to re-run in full puts correct-before-release back on lines you would otherwise have to offset.

## Pass Threshold and KPIs

The run passes only when all three hold; iterate corrections and re-run the checks until they do, or recommend holding the run:

- Every line is either reconciled to the source of truth or on the exception list - no silent gaps.
- Unexplained net variance between recomputed and proposed totals sits at or below the materiality threshold.
- Zero blocker findings outstanding at release.

Track run over run:

- **Payout accuracy rate**: share of lines whose recomputed value matches the proposed value.
- **Exception value** as a share of total run value.
- **Reversal/clawback rate** against planning bands - roughly 2-8% for CPA, under 3% for revenue-share (benchmark ranges, not audited data). Above band → lengthen the validation window or tighten qualification.
- **Post-payout correction rate** - corrections found after money moved.
- **Dispute rate** and dispute resolution time.

## B2B SaaS vs B2C Ecommerce

The discipline is identical: the pending → approved → locked → paid lifecycle, the comparability gate, dedup keys, the exception queue, sampling, and maker-checker apply the same way to both. Audit both with the same sequence. Only three things genuinely diverge:

- **Reversal trigger.** B2C: order return, chargeback, or cancellation inside the return window. B2B SaaS: trial-to-paid failure, refund, downgrade, or mid-period churn.
- **Recurrence.** B2C CPA pays once per order. B2B revenue-share recurs on every billing event - proration, upgrades/downgrades, seat expansion, and the 12-month-cap vs lifetime rule make each cycle a fresh line to verify.
- **Source-of-truth integration.** B2B SaaS platforms often read the billing system directly, so platform data IS billing data - check 4 collapses to a spot-check. B2C networks track independently via pixel, so the full reconciliation stays mandatory.

Do not manufacture other differences; treating the two as separate disciplines duplicates work without catching more errors.

## Failure Modes

| Failure                                       | Why it happens                                                                                                              | Fix                                                                                                |
| --------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------- |
| Tier boundary off-by-one                      | Marginal computed as retroactive (or vice versa); `>=` coded as `>` at the breakpoint; retroactive cliffs magnify the error | Recompute both readings at every boundary line; match the written structure; fix the operator      |
| Duplicate order IDs across sources            | Platform pixel and in-house tracker both record the same conversion                                                         | One canonical source per conversion; shared idempotency key; pay each key once                     |
| Malformed or empty dedup keys                 | Blank transaction IDs collapse many conversions into one record - or none                                                   | Quarantine blank/malformed keys; resolve manually before totaling                                  |
| Conversion counted in two consecutive periods | Re-export overlap or period straddle at the cutoff                                                                          | Half-open period windows; diff this run's keys against the prior run                               |
| Wrong date basis on the export                | Exports offer transaction vs validation vs amendment date; the wrong pick shifts lines across runs                          | Declare the date basis in run scope; re-pull on the declared basis                                 |
| Timezone boundary error at cutoff             | Platform, billing system, and program office each keep a different clock                                                    | Fix one timezone in scope; normalize all timestamps before applying the cutoff                     |
| Clawback window ignored                       | Lines approved before the validation window elapses, so refunds never reverse                                               | Hold every line still inside its window; size the window to the reversal curve                     |
| Refund lands after the record locks           | Locked/paid lines cannot be reversed on-platform                                                                            | Book a forward-period negative adjustment; carry negative balances against future earnings         |
| Recomputed against current terms              | Rates changed since the sale; some platforms re-evaluate modified lines at today's terms                                    | Recompute against the contract terms in force at the original transaction date                     |
| Currency rounding drift                       | Per-step rounding compounds across hundreds of small lines                                                                  | Round to 2 decimals at the end, not each step; drift above materiality → audit the rounding policy |
| Sub-threshold balances silently dropped       | Minimum payout threshold applied as an exclusion instead of a carry-forward                                                 | Verify below-threshold balances roll into the next run; they must never vanish                     |
| Payout released before tax forms on file      | Payee onboarding skipped or forms expired unnoticed                                                                         | Block release for any payee with a missing/expired form; recheck every run                         |

## Invocation Examples

- "Audit this month's payout run before we release it - 840 lines, $61K proposed, platform export and billing export attached."
- "Two affiliates disputed last quarter's commissions. Re-audit that run and tell me whether the tier was applied on marginal or full volume."
- "Our network export and our order database disagree by about $3K. Reconcile them and tell me what to hold."

## Reference

- `mbfinotti/partnerships-skills@affiliate-commission-structure` - for designing the structure this audit verifies against
- `mbfinotti/partnerships-skills@affiliate-fraud-detection` - for the fraud ruleset upstream of the payout calculation
- `mbfinotti/partnerships-skills@affiliate-program-terms` - for the T&C document that codifies validation windows and clawback rights
- `mbfinotti/partnerships-skills@affiliate-performance-dashboard` - for ongoing program metrics, as opposed to a per-run audit