> ## Documentation Index
> Fetch the complete documentation index at: https://docs.dualentry.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Calculate Tax, Then Create an Invoice or Bill

> How to call the V2 tax calculator before creating an invoice or bill, map the calculator result onto each line's tax fields, then post the record.

Public invoice and bill creation does not calculate tax. The DualEntry UI always calculates tax first, then creates the record with the tax result on each line. API clients must follow the same flow.

This guide shows how to call the V2 tax calculator, map its result onto invoice line items, and create the invoice: `POST /public/v2/taxes/calculate/` → map tax fields → `POST /public/v2/invoices/`. The same flow applies to bills using `vendor_id` in place of `customer_id`.

<Warning>
  A shipping address on the invoice alone does not add tax. Pass the buyer address on the calculate call as `transaction_buyer_address` instead. The `shipping_address` you send on the invoice itself is for fulfillment only and has no effect on the tax calculation.
</Warning>

## Prerequisites

* V2 API (`/public/v2/...`)
* `X-API-KEY` header with your organization's API key ([authenticating your requests](/developers/guides/how-to-authenticate-your-requests))
* The company is set up for the correct tax regime (GST, VAT, or US sales tax)
* The customer (or vendor) has `is_taxable` set to `true` when tax should apply

## Step 1: Resolve IDs

Look up the company, customer or vendor, and item IDs the calculate call needs:

| Need                        | Endpoint                        |
| --------------------------- | ------------------------------- |
| Company                     | `GET /public/v2/companies/`     |
| Customer                    | `GET /public/v2/customers/`     |
| Vendor                      | `GET /public/v2/vendors/`       |
| Item                        | `GET /public/v2/items/`         |
| GST rates (optional lookup) | `GET /public/v2/gst-tax-rates/` |
| VAT rates (optional lookup) | `GET /public/v2/vat-rates/`     |

## Step 2: Calculate tax

Send the resolved IDs and item lines to the calculate endpoint:

```bash theme={null}
curl -X POST https://api.dualentry.com/public/v2/taxes/calculate/ \
  -H "X-API-KEY: your_api_key_here" \
  -H "Content-Type: application/json" \
  -d '{
    "company_id": 1,
    "customer_id": 2,
    "transaction_buyer_address": {
      "country": "US",
      "region": "CA",
      "postal_code": "94105",
      "city": "San Francisco",
      "line1": "123 Main St"
    },
    "transaction_date": "2026-09-16",
    "currency_code": "USD",
    "items": [
      {
        "item_id": 3,
        "quantity": "1.000000",
        "unit_price": "100.000000"
      }
    ]
  }'
```

**Required:**

* `company_id`
* `customer_id` (money-in, e.g. invoices) **or** `vendor_id` (money-out, e.g. bills), not both

**Usually also send:**

* `items`: each with `item_id`, `quantity`, and `unit_price` (`item_id` may be `null` for a vendor expense line)
* `transaction_buyer_address`: the destination address, required for US sales tax
* `transaction_date` and `currency_code`: required for US sales tax
* `supply_date`: filters VAT/GST rates by their validity window

The response `type` is one of:

| `type`      | Meaning                                                                            |
| ----------- | ---------------------------------------------------------------------------------- |
| `SALES_TAX` | US sales tax                                                                       |
| `VAT`       | VAT regimes                                                                        |
| `GST`       | Canada, Australia, New Zealand, Singapore, India, and similar GST/GST-like regimes |
| `NONE`      | No tax calculated. HTTP **200** with a `reason`; this is not an error.             |

For **GST** and **VAT**, the first calculate call often returns `available_rates`. Pick a rate, send it on the line as `selected_gst_tax_rate_id` or `selected_vat_rate_id`, then calculate again with that rate selected.

## Step 3: Map the result onto each invoice line

Copy each field from the calculator response onto the matching field in the invoice line's `tax` object:

| Calculator result field    | Invoice line `tax` field |
| -------------------------- | ------------------------ |
| `tax_code`                 | `code`                   |
| `tax_rate`                 | `rate`                   |
| `tax_amount`               | `amount`                 |
| `is_taxable`               | `is_taxable`             |
| `tax_exempt_amount`        | `tax_exempt_amount`      |
| `tax_metadata`             | `tax_metadata`           |
| `selected_vat_rate_id`     | `vat_rate_id`            |
| `selected_gst_tax_rate_id` | `gst_tax_rate_id`        |

Also carry the line amount over: calculator `unit_price` → invoice line `rate`.

## Step 4: Create the invoice

Send the mapped tax fields on each line when you create the invoice:

```bash theme={null}
curl -X POST https://api.dualentry.com/public/v2/invoices/ \
  -H "X-API-KEY: your_api_key_here" \
  -H "Content-Type: application/json" \
  -d '{
    "company_id": 1,
    "customer_id": 2,
    "date": "2026-09-16",
    "due_date": "2026-10-16",
    "currency_iso_4217_code": "USD",
    "exchange_rate": "1.0",
    "record_status": "posted",
    "shipping_address": {
      "country": "US",
      "state": "CA",
      "postal_code": "94105",
      "city": "San Francisco",
      "street": "123 Main St"
    },
    "items": [
      {
        "item_id": 3,
        "quantity": "1.000000",
        "rate": "100.000000",
        "position": 1,
        "tax": {
          "code": "SALES-TAX-US-CA",
          "rate": "0.0875",
          "amount": "8.75",
          "is_taxable": true,
          "tax_exempt_amount": "0.00",
          "tax_metadata": {}
        }
      }
    ]
  }'
```

Use the **real** values returned by your calculate call. The numbers above are examples only. You may still send `shipping_address` on the invoice for fulfillment; it does not replace the calculate step.

## Canada GST example (two calculate calls)

GST and VAT regimes need a second calculate call once a rate is selected.

**Call A: list available rates**

Calculate without a selected rate first to see what DualEntry offers for this address:

```bash theme={null}
curl -X POST https://api.dualentry.com/public/v2/taxes/calculate/ \
  -H "X-API-KEY: your_api_key_here" \
  -H "Content-Type: application/json" \
  -d '{
    "company_id": 1,
    "customer_id": 2,
    "transaction_buyer_address": {
      "country": "CA",
      "region": "ON",
      "postal_code": "M5V 2T6",
      "city": "Toronto",
      "line1": "100 King St W"
    },
    "items": [
      {"item_id": 3, "quantity": "1.000000", "unit_price": "100.000000"}
    ]
  }'
```

This returns `type: "GST"` with `data.place_of_supply` (`"ON"`) and `data.available_rates` listing candidate rate IDs. Pick one (for example, the Ontario HST rate) from `available_rates` or from `GET /public/v2/gst-tax-rates/`.

**Call B: calculate again with the selected rate**

Add the chosen rate ID to the line and calculate again to get the final tax amounts:

```bash theme={null}
curl -X POST https://api.dualentry.com/public/v2/taxes/calculate/ \
  -H "X-API-KEY: your_api_key_here" \
  -H "Content-Type: application/json" \
  -d '{
    "company_id": 1,
    "customer_id": 2,
    "transaction_buyer_address": {
      "country": "CA",
      "region": "ON",
      "postal_code": "M5V 2T6",
      "city": "Toronto",
      "line1": "100 King St W"
    },
    "items": [
      {
        "item_id": 3,
        "quantity": "1.000000",
        "unit_price": "100.000000",
        "selected_gst_tax_rate_id": 42
      }
    ]
  }'
```

Each returned line now has `tax_code`, `tax_rate`, `tax_amount`, and `selected_gst_tax_rate_id` set. Map those onto the invoice as in [Step 3](#step-3-map-the-result-onto-each-invoice-line), then create the invoice using the values from call B.

## US sales tax

1. Send `transaction_buyer_address` (include `region` and `postal_code`), `transaction_date`, and `currency_code` on calculate.
2. The response `type` is `SALES_TAX`.
3. Copy `tax_code`, `tax_rate`, and `tax_amount` onto the line's `tax` object.
4. Create the invoice.

If the buyer address is missing, calculate returns `type: "NONE"` with a `reason`. Fix the address and calculate again before creating the invoice.

## VAT

1. The first calculate call returns `available_rates`.
2. Send `selected_vat_rate_id` on the line and calculate again.
3. Set the invoice line's `tax.vat_rate_id` from the same `selected_vat_rate_id`.
4. Create the invoice.

## If calculate returns an error

The calculate endpoint returns errors for a few common mistakes:

| Cause                                                                        | Response                                                         |
| ---------------------------------------------------------------------------- | ---------------------------------------------------------------- |
| Neither `customer_id` nor `vendor_id` sent                                   | HTTP 400, "Either customer\_id or vendor\_id is required"        |
| Both `customer_id` and `vendor_id` sent                                      | HTTP 400, "Provide customer\_id or vendor\_id, not both"         |
| `company_id`, `customer_id`, or `vendor_id` doesn't match an existing record | HTTP 404                                                         |
| An `item_id` doesn't match an existing item                                  | `type: "NONE"` with a `reason` explaining which item line failed |

Fix the request and calculate again. A `NONE` result is not itself an error: it means DualEntry could not determine tax for this request, and the `reason` field says why.

## Checklist before go-live

* Call calculate **before** creating the invoice or bill
* Pass the buyer/shipping address on calculate as `transaction_buyer_address`
* For GST/VAT, select a rate from `available_rates` and calculate a second time
* Copy every mapped tax field onto each line's `tax` object
* Confirm the invoice response shows `items[].tax` with the expected code, rate, and amount

## Related resources

This guide covers the calculate-then-create flow. Browse the individual V2 resources in the [V2 API reference](/developers/api/resources-v2/invoices/list-invoice-records), including the [calculate tax reference](/developers/api/resources-v2/taxes/calculate-tax), [GST rates](/developers/api/resources-v2/gst-tax-rates/list-gst-tax-rates), and [product tax codes](/developers/api/resources-v2/product-tax-codes/list-product-tax-codes).
