A common question we get is: "if you don't want people to automate X, why not just refuse to ship it?" The honest answer is that we do refuse to ship it — three different times, in three different places, because any single layer can fail.
Layer 1 — Server-side, at workflow save
Every workflow JSON document passes through Zod validation when the user clicks Save. Part of that validation walks the node graph and collects every packageName reference. If any name appears in our denylist, the API returns a BAD_REQUEST with the offending package called out in the error. The builder UI highlights the node so the user knows exactly which step to remove.
This is the cheapest layer to enforce and catches accidental references early. It also produces an audit_log row tagged denylist.violation so we can spot patterns.
Layer 2 — Marketplace review worker
Templates submitted to the marketplace are re-scanned by a background worker before going public. The scan is the same logic the API runs, plus a static analysis pass that looks for patterns we don't pre-block but want to flag (e.g. CAPTCHA selectors, common ad-network method names). Anything that hits is unpublished and queued for human moderation.
Layer 3 — On-device, in the Kotlin interpreter
This is the load-bearing layer. Just before dispatching any UI action that targets a package, the Kotlin interpreter checks the package against a compiled-in Denylist object. That object is generated from the same source-of-truth file as the server-side check via scripts/ts-to-kotlin.ts; CI fails if the two ever drift.
Why is on-device the load-bearing layer? Because anyone with the source could spin up their own API or run an older client. The device can never be tricked into bypassing the gate as long as the APK we ship is the APK that runs. We sign every release.
How additions get on the list
New entries come from three sources: pattern recognition in our abuse queue, reports to abuse@smartordercapture.com, and our own product judgment when a new category of misuse appears in the wild. Additions are batched into the next Android release and announced in the changelog.
We don't accept "delist this app for me" requests. If you want to automate something on the list, smartordercapture is not the right tool.
