Skip to main content
Built for Shopify · self-assessment · v2026-05

Built for Shopify scorecard

A public record of how BulkFlow meets every category of the Built for Shopify requirements. Each row links to the implementation surface — code, settings, or UX — so a reviewer can verify the claim without asking.

32 Met 4 N/A

Last reviewed against BFS criteria on 2026-05-25.

1. Performance

Loads inside the Shopify Admin via App Bridge v4
Embedded with @shopify/app-bridge/actions v4. Session tokens authenticated server-side via backend/core.py::_try_session_token_user.
Initial render < 3s on a cold load
React lazy-loads every route. Code-split by page in App.js. Initial paint hits the AppBridge boot frame, then suspends until /billing/me resolves.
No layout shift after data load
Skeleton placeholders fill the exact final dimensions (components/Primitives.jsx::TableSkeleton / CardSkeleton) so rows don't jump when real data lands.
Lighthouse score ≥ 70 across categories
Verified against production bulkflow.app. Re-run after every release; failures block the deploy.

2. App admin (embedded)

No custom login screen for merchants
Merchants authenticate via Shopify session tokens only. Custom /login exists exclusively for SuperAdmin internal access on the raw domain — never linked from any merchant surface.
No 'Sign out' button inside the embedded frame
Layout.jsx hides the SignOut button when window.parent !== window. Merchants sign out via Shopify.
Native look-and-feel matching Polaris
Light-mode UI, Polaris palette tokens 1:1 in index.css. Reviewed against the Polaris design system documentation.
Same brand experience inside & outside the iframe
/welcome (public) and the embedded admin share the same component library, type system, and brand colors.

3. App Bridge

App Bridge v4 (latest)
Pinned in frontend/package.json. We do NOT use the legacy AppBridge React shim.
Session token authentication
Every authenticated API call uses a fresh JWT minted by AppBridge, validated server-side against the Shopify-issued public keys.
Resource picker / toast / contextual save bar where appropriate
Toast surfaces (sonner) match Polaris timing. Contextual save bar present on Settings + bulk-edit modals.

4. Online store

Theme app extension
BulkFlow is an admin-only operations app. We do not write to the storefront theme — variant visibility is driven by Shopify's native inventory rules.
Web Pixel

5. Privacy & data protection

Mandatory GDPR webhooks registered
customers/data_request, customers/redact, shop/redact all wired in backend/routers/phase2.py. Tested in test_iter24_gdpr_webhooks.py.
Data scoped per shop
Every Mongo query funnels through tenant_scope(shop_id) in backend/core.py. Cross-tenant access is impossible at the query layer. Verified in test_multi_tenant_isolation.py.
No customer PII collected
We process Shopify webhook payloads for line-item math (qty × pack_size) only. Customer names/emails/addresses are explicitly stripped before persistence. Confirmed in Privacy Policy §2.
Data deletion on uninstall
shop/redact deletes every BulkFlow row tagged with the merchant's shop_id within 48 hours. The job is idempotent.
Public privacy policy
/privacy — plain-English, no jargon.

6. Security

HMAC-verified webhooks
Every webhook handler validates the X-Shopify-Hmac-Sha256 header before reading the body. Bad HMAC → 401, no further processing.
HTTPS-only
Enforced at the ingress layer. No HTTP fallback path exists.
Secrets stored in environment, not code
All API keys/tokens (Shopify client_secret, Resend, MongoDB, etc.) live in backend/.env — never committed.
Audit log of every inventory-changing action
movements + audit_logs collections track every adjustment with actor identity (Shopify staff ID + email, pulled from the session token), timestamp, source (shopify_session_token / legacy_jwt), and target. Exportable as CSV.

7. Billing

Shopify-managed billing only
All paid plans use appSubscriptionCreate via the Shopify Billing API. BulkFlow never stores a credit card. Cancel from Shopify admin → Apps.
Trial handled by Shopify, not custom timer
trialDays is passed to the GraphQL mutation; Shopify enforces the trial. Verified in backend/routers/billing.py at billing_subscribe.
Free tier with clear upgrade prompts
15-SKU Free tier with live Shopify push-sync included. SKU-cap enforcement returns a 402 with current_plan + next_plan so the upgrade modal can pitch the right tier. Tested in test_full_tier_gate_audit.py.
Public pricing matrix
/welcome#pricing renders the same matrix that drives backend gating (FEATURE_MATRIX_GROUPS in billing.py). Drift between marketing and code is impossible without a test failure.

8. Accessibility

WCAG 2.1 AA conformance target
See full statement at /accessibility.
Keyboard-navigable end-to-end
Every interactive element is a real <button> or <a>. Modals trap focus. Tooltips reveal on focus, not just hover.
Touch targets ≥ 44×44 px
Audit Pass 2 (2026-05-25) added the .row-action utility class to dense table-row icon buttons across Items / Movements / Orders / Transfers. Form controls + standard buttons already enforced via Primitives.jsx sizing.
Screen-reader friendly
Charts have aria-label. Icons inside icon-only buttons carry aria-label. Status icons (✓/✗) in the pricing matrix sit inside SVGs with semantic role.

9. Support & trust

Public support URL with response SLA
/supportsupport@bulkflow.app, 1 business day response.
Public roadmap
/roadmap — what's shipping next, what shipped, what we explicitly won't build.
Public Privacy & Terms
In-app feedback widget
Every admin page renders a FeedbackWidget in the bottom-right; messages route to the same inbox as support@bulkflow.app.

10. App listing & marketing

App listing in 1+ languages
Single-language launch (English). Translations queued post-launch.
App store assets capture pass
Hero screenshots, 60-second journey video, and listing copy live outside the codebase. The /demo?clean=1 route exists specifically for asset capture.