Skip to navigation

Special Rules

TIC-specific tax rules that sit outside the standard rate calculation, starting with meal tax

View as Markdown
Available onProEnterprise

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:

FieldTypeMeaning
combinedMealRatenumber or nullSum of the rates of the unconditional components in rules, as a decimal. It is not the total tax rate. null when no unconditional component applies.
reasonCodesarray of strings or nullQualifications of the result. See Reason codes.
rulesarray or nullThe meal-tax components that apply at the location, widest jurisdiction first. Empty when none apply.

Each entry in rules is one component:

FieldTypeMeaning
jurisdictionTypestringLevel of the levying jurisdiction, such as STATE, COUNTY, INDEPENDENT_CITY, MUNICIPALITY, or SPECIAL_DISTRICT.
jurisdictionNamestringName of the levying jurisdiction.
jurisdictionCodestringGeography code the location matched: a state FIPS code, a state + county FIPS code, a 7-digit Census place GEOID, or a 10-digit Census county subdivision GEOID (Massachusetts and Vermont towns, which aren’t Census places).
taxNamestringName of the tax program.
componentTypestringADD_ON, STATE_SPECIAL_TOTAL, or OCCUPATION_TAX. Decides how the component combines with sales tax. See How to calculate meal tax.
ratenumberComponent rate as a decimal (0.01 = 1%).
replacesStateRatebooleantrue exactly when componentType is STATE_SPECIAL_TOTAL. Despite the name, such a component replaces the whole ordinary sales tax, state and local.
effectiveDtinteger or nullFirst day the component applies, as a YYYYMMDD number (20180101). null when the start date isn’t recorded.
expiresDtinteger or nullLast day the component applies, as a YYYYMMDD number. null when open-ended.
sourcingBasisstringWhere the component is sourced. Independent of the lookup’s sourcingRules. See Sourcing.
conditionalbooleanAlways present. true when the component applies only to some sales at the location.
conditionCodestring or nullMachine-readable reason the component is conditional, such as LIVE_PERFORMANCE. An open set: new codes may be added. null when conditional is false.
conditionDescriptionstring or nullPlain-language description of the condition. null when conditional is false.

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. replacesStateRate is true exactly 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:

rate = occupation + sales × (1 + occupation) + addOns

where:

  • sales is the ordinary SALES_TAX total, or the STATE_SPECIAL_TOTAL rate when one applies.
  • occupation is the sum of the OCCUPATION_TAX rates (0 when there are none).
  • addOns is the sum of the ADD_ON rates.

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:

ValueLocated atMeaning
SELLER_LOCATIONOriginFollows the seller’s location.
SELLER_LOCATION_WITH_CATERING_EXCEPTIONOriginThe seller’s location, except catering, which follows the jurisdiction served (Indiana).
SELLER_LOCATION_REVIEW_DELIVERYOriginThe seller’s location for on-site sales. Delivery treatment is not yet confirmed (Mississippi).
SOLD_OR_SERVED_LOCATIONOriginWhere the meal is sold or served (Louisiana).
EVENT_LOCATIONOriginWhere the event takes place (Mississippi catered sales).
DESTINATIONDestinationWhere meals are provided or delivered (Vermont).
CUSTOMER_RECEIPT_LOCATIONDestinationWhere the customer receives the meal (Wisconsin).
STATE_TAX_SOURCINGCart’s sourcingFollows the state’s ordinary sales tax sourcing.
MEAL_SALE_SOURCINGCart’s sourcingA local meals excise sourced with the meal sale (Massachusetts).
STATEWIDE_EATING_ESTABLISHMENT_SALESCart’s sourcingA statewide levy on eating-establishment sales (Rhode Island).
REVIEW_REQUIREDCart’s sourcingThe rate is on file but its sourcing is not yet confirmed. Says nothing about the rate itself.

“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”.

CodeMeaningWhat to do
LOCATION_NOT_RESOLVEDThe location’s state could not be resolved, so meal tax was not evaluated. An empty rules array is not a finding of no meal tax.Retry with a full address or a lat/lng pair.
RULES_NOT_LOADEDMeal-tax data is unavailable on the server. This is not a finding of no meal tax.Retry later.
STATE_NOT_COVEREDZiptax has no meal-tax rules on file for the state. This is not a finding that the state has none.Confirm the state’s meal tax treatment from another source.
UNMAPPED_SCOPES_IN_STATEAt least one meal-tax district in the state has no mapped boundary. The code is set per state, not per location: every lookup in that state gets it, whatever the address. Today this applies to Illinois, Indiana, Louisiana, Minnesota, Mississippi, and South Dakota.Apply the returned rules. Treat the result as possibly incomplete only if the location being rated (for a cart, the address the component would be sourced at) could be inside one of the state’s unmapped districts, such as Chicago’s MPEA district, Downtown Naperville, the Minneapolis downtown taxing area, or Giants Ridge.

Worked example: Minneapolis

A lookup for a downtown Minneapolis address with TIC 41000:

cURL
curl -G -H "X-API-KEY: $ZIPTAX_API_KEY" \
"https://api.zip-tax.com/request/v60" \
--data-urlencode "address=350 S 5th St, Minneapolis, MN 55415" \
--data-urlencode "taxabilityCode=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.