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:
applicationType | What it lowers | Use it for |
|---|---|---|
PrePayment | What 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.
| Field | Required | What it does |
|---|---|---|
payeeId | Yes | The Payee to recover from. |
applicationType | Yes, for a clawback | PrePayment. Leaving it out creates a PostPayment deduction. |
deductionType | Yes | Custom or AdvanceRepayment for PrePayment. |
name, amount, currency | Yes | A label, the amount to recover, and its currency. It applies only to payables in this currency. |
reason | Yes, for PrePayment | OverCollection, Duplicate, Dispute, ServiceFailure, Goodwill, Acquisition, Succession, UnauthorizedDeduction, Promotional, or Other. |
payableId | No | The payable it relates to. Leave it out to create a balance against the payee. |
disposition | When the payable is paid | Balance, Refund, or OffPlatform. |
detail, memo, lineItems, startDate, endDate, priority, externalId, metadata | No | Optional. 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:writescope. With a person's login session it also needs a recent MFA step-up (HighRiskWriteAction); without it the call returns403withcode: 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 useOffPlatformonce 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
422with the field reasonAmountExceedsRefundable. - 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 aRefundedentry and the deduction completes withcompletionReason: 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
| Field | What it shows |
|---|---|
status | Pending, PartiallyApplied, or Complete. |
completionReason | Why it finished: FullyApplied, ShortApplied, ClosedEarly, Expired, Refunded, or OffPlatform. |
amountApplied, amountRemaining, pendingRefundAmount | What's been taken, what's left, and what's held for a pull-back in progress. |
entries | Every Applied, Unapplied, Refunded, Expired, and Closed amount, with the payable, time, and actor. |
number | The adjustment number, such as DD-000087. A PrePayment deduction shows the same number to the payee. |
accountId, actors, events | Which 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
| Status | When |
|---|---|
401 | The token is missing or invalid. |
403 | The 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. |
404 | The deduction doesn't exist, or the payee isn't one of yours. |
409 | Duplicate 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. |
412 | Your If-Match is out of date. |
422 | The 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
Updated 24 minutes ago