Skip to main content
Withholding tax (WHT) is tax you deduct from a vendor’s payment and pay to a tax agency on the vendor’s behalf. Through the Public API, you can apply withholding tax to bill and direct expense lines, reverse it with vendor credit lines, set vendor defaults, and remit the withheld amounts to a tax agency. The fields on bills, direct expenses, vendor credits, recurring bills, and vendors are available on V1, V2, and V3-alpha. The withholding tax rates, open items, and remittances resources are available on V2 and V3-alpha only. For how withholding tax is calculated and posted, see Withholding Tax.
On a bill, direct expense, or vendor credit with withholding tax, the header amount is the amount payable to the vendor, net of withholding tax. A 1,000.00 bill with 10% withholding tax returns amount "900.00" and withholding_tax_total "100.00". If your integration reads amount as the gross bill total, add withholding_tax_total to it.

Prerequisites

Before you apply withholding tax through the API, confirm the following:
  • API key: Your requests send your organization’s API key in the X-API-KEY header. See authenticating your requests.
  • Withholding tax rates: The rates are set up in the app under Configuration → Tax Setup → Withholding Rates. The API reads rates but can’t create them. See How to Set Up Withholding Tax.
  • Company address: The company has an address with a country. A rate applies only to records of a company in the rate’s country.
  • Accounts and vendors: You have a liability account (type other_current_liability or long_term_liability) for withheld tax, and a vendor with vendor_type tax_agency for the tax authority.

Look up withholding tax rates

Withholding tax rates are read-only in the API. List them to find the id to send as withholding_tax_rate_id on a line. The active_on filter returns only the rates that are valid on a date, which is the record date you plan to use.
Each rate in the response looks like this. The rate is a fraction, so 0.100000 is 10%.
The withholding tax rate endpoints are listed below.

Set a vendor’s withholding tax defaults

A vendor’s withholding fields are writable on create, PUT, and PATCH. On PUT and PATCH, an omitted field keeps its stored value, and null clears an ID field. The following request turns on withholding tax for a vendor and sets its defaults:
The API doesn’t copy the vendor’s default rate onto lines: the app suggests it, but an API client must send withholding_tax_rate_id on each line. The default payable account is used when a bill or direct expense with withholding tax has no withholding_tax_payable_account_id. The vendor withholding tax fields are listed below.

Create a bill with withholding tax

To withhold tax on a bill or direct expense line, send withholding_tax_rate_id on the line. DualEntry calculates the withheld amount as the line’s net amount (before VAT, GST, or sales tax) multiplied by the rate, rounded to two decimal places. The following request creates a posted bill with one 1,000.00 line at 10%:
The response includes the withholding tax on the header and on each line:
If you omit withholding_tax_payable_account_id, or send null, DualEntry uses the vendor’s default_withholding_tax_payable_account_id. A record with withholding tax needs one of the two. Direct expenses take the same fields on their items lines. Recurring bill templates accept them in record_payload, and each bill the template creates gets the withholding tax.

Update withholding tax on a record

On an update, a line keeps its withholding tax unless you send withholding_tax_rate_id for it. DualEntry matches each line you send to its stored line by the line id, then reads withholding_tax_rate_id as follows:
  • Field omitted: The line keeps its stored rate. This stops clients that don’t know about withholding tax from removing it.
  • null: The line’s withholding tax is removed.
  • A rate ID: The line uses the new rate.
  • New line: A line sent with no id is a new line, with only the withholding tax you send.
The header withholding_tax_payable_account_id works the same way: omit it to keep the stored account. Bills support this on PUT and PATCH, and direct expenses on PUT. Vendor credits have no header payable account. A bill PATCH that sends expenses or items replaces that whole list, so include every line you want to keep. The following request removes the withholding tax from line 555:

Reverse withholding tax with a vendor credit

A vendor credit line reverses withholding tax by linking to a bill line with credited_bill_line_id. The line takes the bill line’s rate and payable account, so vendor credits have no withholding_tax_rate_id or header payable account. The reversed amount is the bill line’s rate multiplied by the credit line’s net amount, up to the bill line’s withholding tax that isn’t already reversed. For example, a vendor credit line of 300.00 with "credited_bill_line_id": 555 reverses 30.00 of a 10% bill line. The vendor credit returns amount "270.00", withholding_tax_total "30.00", and a credited_bill_line object with the bill line’s id, bill_id, bill_number, and position. The bill line must be on a posted bill with the same vendor, company, and currency. On a PUT, a line that omits credited_bill_line_id keeps its link, and null removes the link and the withholding tax.

Remit withheld tax to a tax agency

A withholding remittance records a payment to a tax agency for open withheld amounts. A withheld amount is open when its record is posted and it isn’t on a draft or posted remittance. Remit in two steps. First, list the open withheld amounts for a company. Amounts are in the company’s base currency, and vendor credit amounts are negative.
The response lists each open withheld amount:
Then create the remittance with the withheld_entry_id values you’re paying. Each amount is remitted in full.
A remittance created through the API is posted. It debits the liability account and credits the bank account for the total, and locks the source lines. The request body fields are listed below. The withholding tax endpoints are listed below. The remittances resource has no update or archive endpoint: archive a remittance in the app. The same endpoints are available under /public/v3-alpha/.

Permissions

Each endpoint needs a permission on the API key’s role. The open items list and remittance create need permission to create vendor payments, the same as remittances in the app. A key without the permission gets 403. Withholding tax fields on bills, direct expenses, vendor credits, and vendors use those resources’ existing permissions. For an open-items request with a company_id outside the key’s access, the API returns 200 with an empty items list. An API key carries the permissions of the roles you choose when you create it. An integration that only reads withholding tax, such as one that exports remittances, needs the view permissions. An integration that also remits needs vendor payment create. If a call returns 403, add a role with the missing permission to the key, or create a new key with the right roles. The API can’t archive a remittance, so a user with permission to archive vendor payments must do it in the app. See How to Give an Integration Access to DualEntry.

Errors

Withholding tax validation errors return 422. The most common messages are listed below. For the error response format, see Errors.
Last modified on November 10, 2026