Deductions and clawbacks

Lower what you pay a payee with a Wingspan V3 deduction. Recover an overpayment from later payables, pull back a settled payout, or withhold an amount from a payee's payouts.

A deduction lowers what you pay a payee. You create it once, and Wingspan applies it when you pay the payee's payables, recording each application. applicationType decides what the deduction lowers:

applicationTypeWhat it lowersUse it for
PrePaymentWhat you owe on the payable. A payee's credit isn't mirrored as a deduction, so treat the two as separate records.Clawbacks and other commercial adjustments: an overpayment, a job cancelled or cut short after payout, a duplicate payment.
PostPayment (default)The payee's payout only. What you owe stays the same.Benefits, garnishments, child support, tax withholding, and advance repayment.

Neither kind changes a payable's line items or amount. Only the Account that created a deduction can change it. A payee's credit is a separate record in the Credits API. The payer's deduction view doesn't include a payee credit, so don't expect it in your deductions. Deductions need the payments.deduction:read and payments.deduction:write scopes.

Recover an overpayment from later payables

You paid a payee $1,200 for a job, and after the payout the job was cut to $900. Create a PrePayment deduction for the $300 against the payable you already paid. With disposition: Balance, Wingspan takes the $300 from the payee's next payables, and the deduction keeps payableId pointing at the original payable.

curl -X POST "https://api.wingspan.app/v3/payments/deductions" \
  -H "Authorization: Bearer $WINGSPAN_TOKEN" \
  -H "Idempotency-Key: $(uuidgen)" \
  -H "Content-Type: application/json" \
  -d '{
    "payeeId": "q7Lm2VxR9tKd4WnB8sHc1Z",
    "applicationType": "PrePayment",
    "deductionType": "Custom",
    "name": "Job 55102 shortened after payout",
    "reason": "OverCollection",
    "memo": "Paid for 8 hours, worked 6",
    "amount": 300.00,
    "currency": "USD",
    "payableId": "Hb4Xk9Qw2Rt7Vn1Mz8Pc3L",
    "disposition": "Balance",
    "externalId": "clawback-55102",
    "metadata": { "jobId": "55102", "approvedBy": "ops_0042" }
  }'
// (trimmed)
{
  "id": "Dd6Wq1Zk8Tx3Nv5Lm9Rb2H",
  "number": "DD-000087",
  "payeeId": "q7Lm2VxR9tKd4WnB8sHc1Z",
  "payableId": "Hb4Xk9Qw2Rt7Vn1Mz8Pc3L",
  "applicationType": "PrePayment",
  "deductionType": "Custom",
  "reason": "OverCollection",
  "disposition": "Balance",
  "amount": 300.00,
  "currency": "USD",
  "status": "Pending",
  "amountApplied": 0.00,
  "amountRemaining": 300.00,
  "pendingRefundAmount": 0.00,
  "entries": []
}

The payee's next payable is $500. When you pay it, Wingspan applies the deduction and pays the payee $200. The payable keeps its $500 amount and shows the deduction in prePaymentDeductionIds and deductedAmount. The deduction completes with completionReason: FullyApplied. If the next payable were $200, Wingspan would apply $200 and take the last $100 from the payable after that.

FieldRequiredWhat it does
payeeIdYesThe Payee to recover from.
applicationTypeYes, for a clawbackPrePayment. Leaving it out creates a PostPayment deduction.
deductionTypeYesCustom or AdvanceRepayment for PrePayment.
name, amount, currencyYesA label, the amount to recover, and its currency. It applies only to payables in this currency.
reasonYes, for PrePaymentOverCollection, Duplicate, Dispute, ServiceFailure, Goodwill, Acquisition, Succession, UnauthorizedDeduction, Promotional, or Other.
payableIdNoThe payable it relates to. Leave it out to create a balance against the payee.
dispositionWhen the payable is paidBalance, Refund, or OffPlatform.
detail, memo, lineItems, startDate, endDate, priority, externalId, metadataNoOptional. See Create a deduction for each field.

To recover from one specific payable that you haven't paid yet, set payableId to that payable and leave out disposition. Wingspan takes the deduction when you pay it. If the deduction is bigger than the payable, Wingspan closes the rest. Send disposition: Balance to carry the rest to later payables instead.

Pull back a settled payout

To take the money back now instead of from later work, create the deduction on the paid payable with disposition: Refund. Wingspan pulls the amount back from the payee as a refund on that payable, and the payable shows it in refunds[].

curl -X POST "https://api.wingspan.app/v3/payments/deductions" \
  -H "Authorization: Bearer $WINGSPAN_TOKEN" \
  -H "Idempotency-Key: $(uuidgen)" \
  -H "Content-Type: application/json" \
  -d '{
    "payeeId": "q7Lm2VxR9tKd4WnB8sHc1Z",
    "applicationType": "PrePayment",
    "deductionType": "Custom",
    "name": "Job 55102 cancelled after payout",
    "reason": "OverCollection",
    "amount": 300.00,
    "currency": "USD",
    "payableId": "Hb4Xk9Qw2Rt7Vn1Mz8Pc3L",
    "disposition": "Refund"
  }'
  • A pull-back needs the payments.payable-payment:write scope. With a person's login session it also needs a recent MFA step-up (HighRiskWriteAction); without it the call returns 403 with code: StepUpMfaRequired. A backend calling with an API key has no session to step up.
  • The payable must be paid and the payout deposited. Wingspan can't pull back a payout routed to a card, or a payable paid off-platform. Recover those with disposition: Balance, or use OffPlatform once the payee repays you directly.
  • The amount can't exceed what's still refundable on the payable. Pull-backs and any other refund of that payable share one limit. Above it, you get 422 with the field reason AmountExceedsRefundable.
  • While the pull-back runs, Wingspan holds the amount in pendingRefundAmount, so a payable paid at the same time can't also take it. When it settles, Wingspan writes a Refunded entry and the deduction completes with completionReason: Refunded. If it fails, Wingspan releases the hold and the amount can come out of the payee's later payables.

If a payee has stopped working with you, create the deduction on the paid payable with disposition: Balance first, so it keeps that payable as its source. If no payable arrives, call POST /v3/payments/deductions/{deductionId}/complete. Use remainderDisposition: Refund to pull back from the original payable, OffPlatform once the payee repays you outside Wingspan, or Close to write off the rest. The request has the same shape as closing a credit, with the dispositions listed above.

Withhold from a payee's payouts

A PostPayment deduction comes out of the payee's payout. You still owe and fund the full payable, and Wingspan withholds the amount before it pays the payee. Use it for amounts the payee owes someone else or agreed to repay, such as benefits, a garnishment, or an equipment advance. The payee sees the withheld amount on the payment (deductionIds and amountDetails.deductions), and only you can read or change the deduction itself.

curl -X POST "https://api.wingspan.app/v3/payments/deductions" \
  -H "Authorization: Bearer $WINGSPAN_TOKEN" \
  -H "Idempotency-Key: $(uuidgen)" \
  -H "Content-Type: application/json" \
  -d '{
    "payeeId": "q7Lm2VxR9tKd4WnB8sHc1Z",
    "deductionType": "AdvanceRepayment",
    "name": "Equipment advance",
    "detail": "Tablet issued 2026-09-01",
    "amount": 300.00,
    "currency": "USD",
    "priority": 1
  }'
// (trimmed)
{
  "id": "Dd2Nq7Wx4Kt9Lm1Rv6Bz3H",
  "payeeId": "q7Lm2VxR9tKd4WnB8sHc1Z",
  "applicationType": "PostPayment",
  "deductionType": "AdvanceRepayment",
  "name": "Equipment advance",
  "amount": 300.00,
  "currency": "USD",
  "status": "Pending",
  "amountApplied": 0.00,
  "amountRemaining": 300.00,
  "entries": []
}

deductionType is Garnishment, ChildSupport, AdvanceRepayment, Benefits, TaxWithholding, or Custom. A PostPayment deduction appears on each payable's deductionIds and in amountDetails.deductions. To withhold by rule on every matching payable, use a recurring deduction. List, read, update, delete, pause, resume, and cancel rules with the /v3/payments/recurring-deductions endpoints. Only the creator can change a rule. A payroll rule names the type and taxTreatment (PreTax or PostTax) and can take a percentage of gross, net, or disposable earnings, with an optional disbursement to a third party such as a state agency. A rule with applicationType: PrePayment creates commercial deductions instead and needs a reason.

Important: Wingspan applies the deduction mechanically; it doesn't decide whether a deduction is lawful or how much you may withhold. Garnishments, child support orders, benefits, and tax withholding have legal rules about amounts and timing. Confirm your obligations with a qualified professional. This isn't legal or tax advice.

How Wingspan applies deductions

When several PrePayment deductions can apply to one payable, Wingspan uses those that target the payable first, then balances. Within each group, the lowest priority goes first, and a missing priority counts as 0. Then the earliest endDate, with no end date last. Then the oldest deduction. Wingspan caps each application at the payable's unpaid amount and at what's left on the deduction. Wingspan applies deductions to one payable at a time, so two payables paid together can't recover the same amount twice.

startDate and endDate are plain dates in UTC, and both are inclusive. After the endDate, Wingspan expires whatever is left. Expiry never touches an amount held for a pull-back in progress.

If you cancel a payable, or it's refunded in full, Wingspan gives back what the deduction took from it, once. An open deduction gets an Unapplied entry. A Complete deduction stays as it is, and Wingspan creates a new balance deduction linked by sourceAdjustmentId. A returned or failed payment doesn't unapply the deduction. The Applied entry stays on that payable, and Wingspan neither gives the amount back nor applies it a second time.

Reportable earnings follow the year the cash moves. An amount recovered from a later payable lowers the payee's earnings in the year of that later payable.

Track a deduction

FieldWhat it shows
statusPending, PartiallyApplied, or Complete.
completionReasonWhy it finished: FullyApplied, ShortApplied, ClosedEarly, Expired, Refunded, or OffPlatform.
amountApplied, amountRemaining, pendingRefundAmountWhat's been taken, what's left, and what's held for a pull-back in progress.
entriesEvery Applied, Unapplied, Refunded, Expired, and Closed amount, with the payable, time, and actor.
numberThe adjustment number, such as DD-000087. A PrePayment deduction shows the same number to the payee.
accountId, actors, eventsWhich Account created it, who created, updated, and completed it, and when.

GET /v3/payments/payees/{payeeId}/deduction-balance returns, per currency, available (what Wingspan can take from the next payable now) and notStarted (deductions whose startDate is still ahead), as of asOf. PostPayment and Complete deductions aren't counted. GET /v3/payments/deductions/{deductionId}/pdf redirects to a PDF of the deduction.

Find deductions with GET /v3/payments/deductions, filtered by payeeId, status, applicationType, externalId, isActive, or up to 100 ids. Prefer status to isActive, which is true until Complete:

curl -g "https://api.wingspan.app/v3/payments/deductions?filter[payeeId][eq]=q7Lm2VxR9tKd4WnB8sHc1Z&filter[applicationType][eq]=PrePayment&filter[status][anyOf][]=Pending&filter[status][anyOf][]=PartiallyApplied" \
  -H "Authorization: Bearer $WINGSPAN_TOKEN"

Deductions also return the older application[], originalAmount, isActive, and sourcePayableId fields. Use entries, the amount fields, status, and payableId for new work.

Change, close, or delete a deduction

PATCH /v3/payments/deductions/{deductionId} changes name, detail, memo, externalId, metadata, priority, amount, startDate, and endDate. Once a PrePayment deduction has an entry, its amount and dates are locked. Send the ETag from your last read in If-Match. If the deduction changed since then, you get 412 with the current ETag.

POST /v3/payments/deductions/{deductionId}/complete finishes a deduction early with remainderDisposition of Refund, OffPlatform, or Close. Send an amount to finish only part of what's left. Without one, Wingspan uses everything that's available. It needs an Idempotency-Key. With a person's login session it needs a recent MFA step-up (HighRiskWriteAction) for every disposition, not only Refund. A backend calling with an API key has no session to step up. Refund also needs the payments.payable-payment:write scope and a paid payable as the deduction's source.

DELETE /v3/payments/deductions/{deductionId} works only while the deduction is Pending with no entries and no pull-back in progress. Otherwise it returns 409. Use /complete instead.

Deductions from a bulk import

In a PayableImport batch, a row with a negative amount and a status other than Paid creates a deduction against that payee instead of a payable. The batch summary counts deductions separately, and the item's result holds the deductionId.

Deductions in payroll runs

A Contractor payroll run's breakdown reports deductionsCount and deductionsAmount for the payables in the run. See Payroll runs.

Webhooks

You can subscribe to Deduction.Created and Deduction.Updated. Read the deduction with GET /v3/payments/deductions/{deductionId} to follow its entries. A pull-back's payable refund events, such as Payable.RefundCompleted, are also available.

Errors

StatusWhen
401The token is missing or invalid.
403The scope is missing, the payee tried to change your deduction, or a pull-back from a login session lacks a recent MFA step-up (StepUpMfaRequired), or the payments.payable-payment:write scope is missing.
404The deduction doesn't exist, or the payee isn't one of yours.
409Duplicate externalId, an Idempotency-Key reused with a different body, a change to a Complete deduction, or a delete once the deduction has an entry or a pull-back in progress.
412Your If-Match is out of date.
422The amount is zero or less, lineItems don't add up to amount, endDate is before startDate, PrePayment is missing reason or uses a payroll deductionType, a paid payable has no disposition, Refund or OffPlatform is sent with no payableId or an unpaid one, Refund targets a card or off-platform payout, a pull-back exceeds the refundable amount (AmountExceedsRefundable), or a /complete amount is above what's available.

Related pages


Did this page help you?