State Law Check

Integrations

State Law Check answers one question: may this product ship to this address, and what does the law require? These are the ways to put that answer where your orders already go.

Every option below uses the same rules and the same API. You need a free API key for all of them except Shopify. Answers are information, not legal advice.

Shopify (coming soon)

The State Law Check Shopify app will check each cart against the shipping address at checkout and stop orders that can't legally ship there, with a plain message for the shopper. It will also ask for an age check where the law requires one. You'll set it up from your Shopify admin; no code needed.

Until it launches, you can check your whole catalog from a Shopify product export.

WooCommerce

The State Law Check plugin for WooCommerce checks each order against the shipping address at checkout, in both the classic checkout and the Checkout block. When an item can't legally ship there, checkout stops with a message for that item. You can choose to warn instead: the order goes through with an order note. Items with a minimum age aren't blocked; the order note gives the age to verify. Each order note includes the checkId from your decision log (GET /v1/checks).

Your secret key stays on your server. A test key (lts_test_…) works too, with fictional states only. The plugin is open source (Apache-2.0) and lives in clients/woocommerce in our repository. It isn't on WordPress.org yet; until it is, install it from a zip of that folder.

EasyPost

The @statelawcheck/easypost add-on runs a check before you buy a label. It tells you not to buy the label when the law forbids the shipment. When adult signature is required, it gives you the EasyPost delivery_confirmation option to add.

import { checkBeforeLabel } from "@statelawcheck/easypost";

const check = await checkBeforeLabel({
  to_address: { zip: "10001", state: "NY" },
  from_address: { state: "TX" },
  carrier: "UPSDAP",
  product: { category: "folding_knife", mechanism: "assisted", bladeLengthIn: 3.5 },
});
if (!check.ok) throw new Error(check.message ?? "Can't ship this order");
shipment.options = { ...shipment.options, ...check.options };

It takes a plain object shaped like an EasyPost shipment, so it works with any EasyPost client version. assertShippable does the same check and throws when the shipment can't go. It isn't on npm yet; it will be published with the SDKs.

ShipperHQ, Zonos and other rate or checkout platforms

Platforms such as ShipperHQ and Zonos keep their own rules for which products can ship where. Instead of checking every order live, build those rules from a restriction table. POST /v1/matrix checks up to 200 products against all 50 states and DC in one request. It also returns the cities and counties, by ZIP prefix, where the answer differs from the state's.

curl -X POST "https://statelawcheck.com/v1/matrix?format=csv" \
  -H "x-api-key: YOUR_KEY" -H "content-type: application/json" \
  -d '{"originState": "TX", "carriers": ["ups"],
       "items": [{"ref": "sku-1", "attributes": {"category": "folding_knife", "mechanism": "automatic", "bladeLengthIn": 3.4}}]}'

The CSV has one row per product and jurisdiction:

ref,jurisdiction,name,level,state,zip_prefixes,decision,minimum_age,ship,avoid_carriers,licenses_needed,codes,coverage,error
sku-1,US-NY,New York,state,NY,,block,,false,,,BLOCKED_AUTOMATIC_KNIFE BLOCKED_INTERSTATE,restricted,
sku-1,US-TX,Texas,state,TX,,allow,,true,,,,restricted,

Example rows only. Draft rules (not yet reviewed by a lawyer) are applied by default and each cell says its basis; send "includeDrafts": false for reviewed rules only.

How to turn the table into rules:

Laws change. Rebuild the table when GET /v1/changes shows a new ruleset version, or when a webhook tells you one was published. Use the JSON form (without format=csv) to load the table into your own code.

Age verification (AgeChecker.Net, Veratad and others)

State Law Check tells you where an age check applies; an age verification service checks the buyer. For each order, /v1/check returns actions.minimumAge (16, 18, 19 or 21, or null when no age rule applies). Pass that number to your verification provider, and only run the check when it isn't null.

// lts = new StateLawCheck({ apiKey }) from @statelawcheck/sdk
const res = await lts.check({
  destination: { province: order.state, zip: order.zip },
  items: order.items.map((i) => ({ ref: i.sku, productType: i.type })),
});
if (res.decision === "block") return refuse(res.message);
const age = res.actions.minimumAge;   // 16, 18, 19, 21 or null
if (age !== null) {
  // Your own function that starts the provider's check for this age.
  await requireAgeVerification(order, age);
}

Each provider sets the required age in its own way. AgeChecker.Net, for example, lets you set minimum ages by location, product tag or customer group in its dashboard, and its developer docs are in your account. Veratad sets age thresholds in its verification workflows. Use our answer to choose which products and destinations need a check, and at what age. Then set the provider's rules to match, or pass the age through its API where it supports that. Check the provider's own documentation for the exact setting.

Carrier adult signature checks 21+ only. So when minimumAge is 18 or 19, use an age verification service at checkout; adult signature on its own isn't the right check.

Claude and other AI assistants (MCP)

The @statelawcheck/mcp server lets Claude and other assistants that support the Model Context Protocol check shipping, cosmetic ingredients and the law library, with citations. Its tools are read-only.

claude mcp add statelawcheck --env STATELAWCHECK_API_KEY=your_key -- npx -y @statelawcheck/mcp

Setup for Claude Desktop and other clients is in the package README. It will be published with the SDKs.

Webhooks

Get notified when the rules change, so you can rebuild restriction tables or re-check open orders. See webhooks in the API docs.

SDKs

Node (@statelawcheck/sdk) and Python (statelawcheck) clients wrap every endpoint, including matrix, and retry on rate limits. See SDKs and tools.