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).
- Map each restricted product on its State Law Check tab: product type, or category and mechanism, with blade length and spray size. Check now shows the answer in every state.
- Set your ship-from state, carriers, licenses and store rules once, under WooCommerce → Settings → State Law Check.
- If the API can't be reached, orders go through and you get an admin notice (fail open). You can switch this to stop checkout instead (fail closed).
- Optional: show the "Can I ship this here?" widget on product pages, with a widget key.
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:
- block: in ShipperHQ, put the SKU in a shipping group and add a restriction for that state. In Zonos, add a restriction for that product and state. For a
cityrow, restrict the ZIP codes that start with the listedzip_prefixes. - block with
ship=true: only some carriers are restricted. Hide the carriers listed inavoid_carriersfor that state, rather than the product. - age_verify: allow the sale, but require an age check (
minimum_age) and, for 21+, carrier adult signature. - licenses_needed: the sale is legal only with that license. Once you hold it, send its id in
licensesand rebuild the table. - coverage =
not_checked: we haven't researched that state for this product yet. Treat "allow" there as unknown, not as clearance.
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.