Special Rules
TIC-specific tax rules that sit outside the standard rate calculation, starting with meal tax
Some product categories carry taxes that don’t fit the standard rate
breakdown. A restaurant meal can owe a local meals tax on top of sales
tax, or a state meals rate that replaces sales tax entirely. Ziptax
reports these in productDetail.taxabilityCode.specialRules, next to
the TIC’s rateRules.
specialRules is the container for every rule of this kind. Each rule
type has its own field inside it and its own section on this page.
Meal tax is the first. The specialRules object is present only when
one of its rule types applies to the TIC you sent.
Meal tax
When meal tax appears
Send taxabilityCode=41000 or taxabilityCode=41041 (prepared food)
on a GET /request/v60 call. No other request parameter is needed.
The response then carries productDetail.taxabilityCode.specialRules.mealTax.
Any other TIC gets no mealTax object.
Meal tax is reported on its own. It never changes baseRates,
taxSummaries, or rateRules. The SALES_TAX total in taxSummaries
is still the ordinary sales tax rate at the location, and you combine
the two yourself (see How to calculate meal tax).
Response fields
mealTax has three fields:
Each entry in rules is one component:
conditional, conditionCode, and conditionDescription are being
added to the API and may not appear in responses yet.
Unlike rateRules, whose dates are strings, effectiveDt and
expiresDt on a meal-tax component are integers.
How to calculate meal tax
Start from the ordinary sales tax rate, the SALES_TAX total in
taxSummaries. Each component’s componentType decides how it adjusts
that rate:
ADD_ON: added on top of the ordinary sales tax rate. It is not grossed up.STATE_SPECIAL_TOTAL: replaces the whole ordinary sales tax, state and local, whether the meals rate is higher or lower than the ordinary rate. Connecticut, the District of Columbia, Maine, New Hampshire, and Vermont use this type.replacesStateRateistrueexactly for these components.OCCUPATION_TAX: a levy on the seller. It is added on its own and also enters the sales tax base, so sales tax is charged on it.
Put together, the meal rate is:
where:
salesis the ordinarySALES_TAXtotal, or theSTATE_SPECIAL_TOTALrate when one applies.occupationis the sum of theOCCUPATION_TAXrates (0 when there are none).addOnsis the sum of theADD_ONrates.
Example: Vermont. The ordinary sales tax rate at a Vermont location
with a local option tax is 7%. Its meal-tax rules are the 9% state meals
tax (STATE_SPECIAL_TOTAL) and a 1% local meals tax (ADD_ON). The 9%
replaces the whole 7%, so the meal rate is 9% + 1% = 10%, not
7% + 9% + 1%.
Example: an occupation tax. Omaha, Nebraska levies a 2.5% restaurant
occupation tax (OCCUPATION_TAX). Assuming, for illustration only, an
ordinary sales tax rate of 7%, the meal rate is
0.025 + 0.07 × (1 + 0.025) = 0.09675, or 9.675%. Use the SALES_TAX
total from your own lookup in place of the assumed 7%.
combinedMealRate is the sum of the meal-tax components only. It is not
the total tax rate. Don’t add it to the SALES_TAX total when a
STATE_SPECIAL_TOTAL component is present, because that component
replaces the ordinary sales tax instead of adding to it.
Conditional components are listed in rules but not counted in
combinedMealRate. When any component has conditional: true, decide
from its conditionCode and conditionDescription whether it applies to
the sale, and sum the components you apply yourself rather than relying
on combinedMealRate.
Ziptax’s cart endpoints, /calculate/cart
and /merchant/cart/calculate,
apply meal tax to a 41000 or 41041 line this way and skip conditional
components.
Sourcing
Sourcing only matters for deliveries, or for orders that cross a
jurisdiction line. A meal sold and eaten on the premises is taxed where
the sale happens, whatever sourcingBasis says.
For other sales, sourcingBasis says which location a component
follows:
“Cart’s sourcing” means the address the cart is sourced at: the origin
or the destination, as the lookup’s sourcingRules says. An interstate
cart reads only the destination, so a component located at the origin is
dropped. New values may be added. Treat a value you don’t recognize as
the cart’s sourcing.
Reason codes
reasonCodes qualifies the result. Check it before reading an empty
rules array as “no meal tax”.
Worked example: Minneapolis
A lookup for a downtown Minneapolis address with TIC 41000:
rules holds one component, Minneapolis’s 3% entertainment tax:
componentType ADD_ON, rate 0.03, sourcingBasis REVIEW_REQUIRED.
It applies only to some sales, so it has conditional: true,
conditionCode: "LIVE_PERFORMANCE", and conditionDescription “Applies
only to food and drinks sold in public places during live performances.”
It is the only rule matched in Minneapolis, and it is conditional, so
combinedMealRate is null. For a sale in a public place during a live
performance, add 3% to the ordinary SALES_TAX total. Otherwise, the
meal is taxed at the ordinary rate.
reasonCodes includes UNMAPPED_SCOPES_IN_STATE because the Minneapolis
downtown restaurant tax district has no mapped boundary. A seller inside
that district may owe a tax this result doesn’t list.