Install
Five stacks, one gate.
Cleared sits between checkout and the carrier. It mounts as a checkout step where the platform allows one and as a post-checkout hold where it does not, and it never touches your payment flow.
- Where it runs
- A checkout step, or a hold on the order after checkout.
- What it writes
- Two attributes on the order: a status and a record id.
- What fulfillment gets
- An adult-signature flag and the statutory sentence for the label.
Platforms
Where the gate mounts.
The install differs in where the platform lets the gate interrupt the buyer. Everything after that — classification, notice, verification, address match, label — is the same code on every platform.
Shopify
Checkout step on Plus- Where the gate mounts
- On Plus, a checkout UI extension runs the gate before payment. On standard Shopify the checkout is closed, so the gate runs as a post-checkout hold: the order is captured, then held until the buyer finishes verification from a link in the confirmation email.
- What Cleared writes back
- Two order attributes. Staff see them on the order page; Flow can route on them.
- cleared_statuscleared_record_id
- Fulfillment
- ShipStation reads the status as a tag, or EasyPost sets delivery_confirmation to ADULT_SIGNATURE at label creation.
- Install effort
- About a week, including a test order in a development store.
WooCommerce
Checkout step- Where the gate mounts
- The gate renders as a checkout block above the payment section and blocks submission until the evaluation clears, using woocommerce_checkout_process.
- What Cleared writes back
- Two order meta keys, readable from the order admin and the REST API.
- _cleared_status_cleared_record_id
- Fulfillment
- The signature flag is set on whichever label plugin the store already uses; EasyPost is supported directly.
- Install effort
- Two to three days. One plugin, one filter.
BigCommerce
Post-checkout hold- Where the gate mounts
- Cleared subscribes to store/order/created. The order is created as normal and held in Awaiting Fulfillment until the buyer completes the gate.
- What Cleared writes back
- Two custom order fields, plus a note on the order timeline.
- cleared_statuscleared_record_id
- Fulfillment
- ShipStation through the BigCommerce connector; the adult-signature flag rides on the order tag.
- Install effort
- About a week.
Magento / Adobe Commerce
Checkout step- Where the gate mounts
- A React checkout step rendered before payment, mounted into the existing checkout bundle. Nothing else in the checkout moves.
- What Cleared writes back
- Two attributes on sales_order, so the existing fulfillment queries keep working unchanged.
- cleared_statuscleared_record_id
- Fulfillment
- The attributes drive the USPS Priority label; adult signature is added at label creation.
- Install effort
- Estimated two weeks. This is the path costed for Seekins Precision.
Custom (Next.js, React Router, headless)
Either- Where the gate mounts
- A route before payment, or a server-side hold after it, whichever fits the order model you already have.
- What Cleared writes back
- Whatever your order model calls them. The record id is the only field Cleared needs back.
- cleared_statuscleared_record_id
- Fulfillment
- EasyPost or ShipStation called directly from the record.
- Install effort
- Hand-tailored by us. Scoped after a read of your checkout.
API
Three calls and a webhook.
A custom checkout can do everything the adapters do. Calls are server-to-server with a bearer key; the browser never sees it. The base URL is https://api.cleared.example/v1.
POST /v1/evaluate
Classify a cart for a destination. Returns the outcome, one classified line per SKU with its basis and reason, and the requirements the gate will raise. Call it as the cart changes; it is pure and cheap.
Pick a cart
Two gas-gun parts under Seekins policy. The gate applies and the six requirements come back with their cites.
Request
POST /api/evaluate
Content-Type: application/json
{
"lines": [
{
"sku": "0010530053 - F",
"qty": 1
},
{
"sku": "low_pro_adj_gas_blk_config",
"qty": 1
}
],
"shipTo": {
"name": "Connor Callison",
"line1": "2200 Harbor Ave",
"city": "Eureka",
"state": "CA",
"zip": "95501"
},
"accountType": "consumer"
}Response
from this build{
"outcome": "gate",
"jurisdiction": "CA",
"lines": [
{
"sku": "0010530053 - F",
"name": "NOXs Rail System",
"class": "gate",
"basis": "policy",
"reason": "Conservative default: AR-pattern parts run through the gate until each SKU is reviewed."
},
{
"sku": "low_pro_adj_gas_blk_config",
"name": "Low Profile Adjustable Gas Block",
"class": "gate",
"basis": "policy",
"reason": "Conservative default: AR-pattern parts run through the gate until each SKU is reviewed."
}
],
"requirements": [
{
"id": "notice",
"label": "Statutory notice",
"statute": "§3273.51(e)(1)(A)"
},
{
"id": "attestation",
"label": "Written attestation",
"statute": "§3273.51(e)(1)(B)"
},
{
"id": "id_verification",
"label": "Identity and age verification",
"statute": "§3273.51(e)(1)(C)"
},
{
"id": "address_match",
"label": "Delivery address matches the ID",
"statute": "§3273.51(e)(3)(B)"
},
{
"id": "label",
"label": "Statutory sentence on the label",
"statute": "§3273.51(e)(3)(A)"
},
{
"id": "adult_signature",
"label": "Adult signature on delivery",
"statute": "§3273.51(e)(3)(C)"
}
],
"fflLines": []
}This posts the request above to /api/evaluate in this app and prints what comes back.
POST /v1/verifications
SimulatedStart the identity and age check for a record and get back a hosted URL to send the buyer to.
Request
POST /v1/verifications
Authorization: Bearer sk_live_…
Content-Type: application/json
{
"recordId": "rec_sim_8f21c4",
"provider": "stripe_identity",
"returnUrl": "https://store.example/checkout/cleared"
}Response
{
"id": "vs_sim_8f21c4d0",
"status": "processing",
"provider": "simulated",
"hostedUrl": "https://verify.cleared.example/s/vs_sim_8f21c4d0",
"expiresAt": "2026-01-22T18:41:00.000Z"
}In this demo the provider is always “simulated” and the document never leaves the browser. In production this call opens Stripe Identity or Veriff and the document never touches the retailer’s servers.
GET /v1/records/:id
Read the durable record for one order: what was shown, what was attested, what was verified, what went on the label, and when. This is the object the console renders and the CSV export flattens.
{
"id": "rec_sim_8f21c4",
"orderId": "ord_sp24831",
"createdAt": "2026-01-22T18:44:12.000Z",
"jurisdiction": "CA",
"skus": [
"0010530053 - F",
"low_pro_adj_gas_blk_config"
],
"outcome": "gate",
"status": "cleared",
"noticeVersion": "ca-ab1263-2026-01",
"attestation": {
"at": "2026-01-22T18:42:39.000Z",
"ip": "203.0.113.24",
"initials": "JE"
},
"verification": {
"status": "verified",
"sessionId": "vs_sim_8f21c4d0",
"provider": "simulated",
"firstName": "Connor",
"lastName": "Callison",
"dob": "1991-04-12",
"age": 34
},
"addressMatch": {
"matched": true,
"normalizedShipTo": {
"name": "JORDAN ELLIS",
"line1": "2200 HARBOR AVE",
"city": "EUREKA",
"state": "CA",
"zip": "95501"
},
"normalizedId": {
"name": "JORDAN ELLIS",
"line1": "2200 HARBOR AVE",
"city": "EUREKA",
"state": "CA",
"zip": "95501"
},
"mismatches": []
},
"carrier": {
"service": "USPS Priority Mail",
"adultSignature": true,
"labelText": "Signature and proof of identification of person aged 18 years or older required for delivery."
},
"timeline": [
{
"at": "2026-01-22T18:41:02.000Z",
"event": "Evaluated",
"detail": "2 lines, gate required"
},
{
"at": "2026-01-22T18:41:44.000Z",
"event": "Notice shown",
"detail": "ca-ab1263-2026-01"
},
{
"at": "2026-01-22T18:42:39.000Z",
"event": "Attested",
"detail": "JE"
},
{
"at": "2026-01-22T18:43:30.000Z",
"event": "Identity verified",
"detail": "Age 34"
},
{
"at": "2026-01-22T18:43:58.000Z",
"event": "Address matched"
},
{
"at": "2026-01-22T18:44:12.000Z",
"event": "Cleared",
"detail": "Adult signature required"
}
]
}Webhooks
Cleared posts to your endpoint as an order moves. Each delivery is signed; retries back off for 24 hours.
| Event | Sent when |
|---|---|
| evaluation.completed | A cart has been classified. Carries the outcome and the line classes. |
| verification.completed | The identity and age check finished, passed or failed. |
| order.cleared | Every requirement in the retailer’s policy is recorded as satisfied. |
| order.held | The address did not match the ID, or a step was abandoned. |
| order.blocked | The order cannot be completed to this destination. |
| order.routed_to_dealer | The shipment goes to a licensed dealer instead of the buyer. |
| record.created | The compliance record is durable and exportable. |
POST https://store.example/webhooks/cleared
Cleared-Signature: t=1769107452,v1=…
{
"id": "evt_sim_31a9",
"type": "order.cleared",
"createdAt": "2026-01-22T18:44:12.000Z",
"data": {
"orderId": "ord_sp24831",
"recordId": "rec_sim_8f21c4",
"status": "cleared",
"outcome": "gate",
"adultSignature": true
}
}Next
See the whole thing run on a real catalog.
The Seekins workspace has 79 products classified under California rules and ten scenarios that each run end to end through the gate.