Ready to maximize your revenue?Book a Demo
TOPCODE
App Store

5 mistakes that get apps rejected from the App Store

Last updated on June 2, 2026

5 mistakes that get apps rejected from the App Store

TOPCODE

Getting rejected from the Shopify App Store after weeks of development is one of the more demoralizing experiences in the app ecosystem. What makes it worse is that most rejections are avoidable — they stem from a small set of recurring mistakes that reviewers see over and over. This list covers the five mistakes that cause the most App Store rejections in 2026, based on Shopify's published review requirements and the patterns that show up in developer forums after rejection notices.

  1. Requesting more OAuth scopes than the app actually uses

    This is the most common rejection reason for new apps. Shopify reviewers check every scope declared in your shopify.app.toml against the actual API calls your app makes. If you request write_customers but your app only ever reads customer emails to send a confirmation, that's an excess scope. Reviewers are trained to look for this and will reject the app with a request to remove the unused scope.

    The fix is straightforward but requires discipline: audit your API calls before submission and delete any scope not directly used. If you're using the @shopify/shopify-app-remix package, run a search across your codebase for every Admin GraphQL mutation and REST write call, then cross-reference with the scopes table in the Shopify docs. A mutation like orderUpdate requires write_orders. If you don't use that mutation, remove write_orders from your scopes.

    A trap to watch out for: scaffold templates often include placeholder scopes (write_products, read_customers) that seem harmless to leave in. They're not. Remove every scope you didn't add deliberately for a specific feature.

  2. Failing the embedded app UI requirements

    Shopify requires embedded apps to use Polaris (or be visually consistent with it) and to follow the Admin design guidelines. Reviewers check for things like: custom typography that doesn't match Polaris, buttons that don't use standard Polaris variant patterns, form inputs that don't follow Polaris validation conventions, and page layouts that use raw CSS rather than Polaris layout primitives.

    The specific items reviewers flag most often: multiple primary buttons on a single page (only one is allowed per context), loading states that use a custom spinner instead of Polaris's Spinner or SkeletonPage, empty state pages that don't use Polaris's EmptyState component, and modals built with custom HTML instead of Polaris's Modal component.

    Before submitting, navigate through every page of your app and compare it to similar pages in the native Shopify Admin. If a page in your app looks substantially different — especially in typography, spacing, or interaction patterns — it will likely be flagged.

  3. Missing or incomplete GDPR webhook handlers

    Every App Store app must implement three GDPR-related mandatory webhooks: customers/data_request (a customer has asked for their data), customers/redact (a customer has requested data deletion), and shop/redact (a merchant has uninstalled your app and their shop data must be deleted). These are required even if your app doesn't store any customer PII — you must at minimum acknowledge receipt and log that you processed the request.

    Reviewers test these webhooks during the review process. They will fire them and check that your endpoints return a 200 response within a reasonable time. A common failure mode is apps that have the webhook routes defined but return 500 errors because the handlers try to delete records that don't exist for the test store. Make your handlers idempotent — if there's nothing to delete, still return 200.

    Also verify your webhook HMAC validation is correct. Shopify signs every webhook with your app's client secret. If your HMAC validation code has a bug, the webhook handler will reject the test webhook with a 401, which is an automatic rejection trigger. The @shopify/shopify-app-remix package includes built-in webhook handling with HMAC validation — use it rather than rolling your own.

  4. Broken or incomplete onboarding flow

    Shopify reviewers install your app on a fresh development store with no existing data. If your app's onboarding assumes pre-existing products, customers, or orders, the reviewer will see an empty state or an error. Apps are rejected for showing JavaScript errors on first load, for empty screens with no guidance, or for onboarding flows that get stuck because required data doesn't exist yet.

    Build an explicit empty state for every resource page. Polaris's EmptyState component is designed exactly for this. Show a meaningful message and a primary action — 'Create your first campaign', 'Connect your account', 'Import products'. If your app requires a configuration step before it can function, make that configuration step unavoidable on first install.

    Test your own app on a fresh store with zero products the week before submission. You will almost certainly find at least one page that breaks or shows no data without a helpful empty state. Fix it before the reviewer finds it.

  5. Incorrect billing implementation

    If your app charges merchants, the billing integration is scrutinized carefully. The most common billing-related rejection reasons are: blocking app functionality entirely before a trial has expired (Shopify requires apps to offer a free trial and not gate features during it), charging merchants outside the Shopify Billing API (you must use Shopify's billing system — not Stripe, not PayPal, not a direct invoice), and failing to handle the case where a merchant declines the charge.

    When a merchant declines a recurring charge, your app receives a callback with the charge status set to DECLINED. Your app must handle this gracefully — typically by showing the merchant the pricing page again with a clear explanation, not by crashing or locking them into a broken state. Similarly, when the charge is ACCEPTED, redirect the merchant back into your app immediately rather than leaving them on the Shopify billing confirmation page with no next step.

    Also check that your app's pricing page accurately reflects what's included in each plan. If your app listing says 'Basic plan includes up to 100 orders/month' but the actual plan in the Admin Billing API doesn't have that cap enforced, reviewers may flag a mismatch between listed features and implementation.

Pre-submission self-check

Run through this checklist the week before you submit:

  • Every scope in shopify.app.toml is used by at least one actual API call — verify with a codebase search
  • Install on a fresh store with zero data and click through every page — no errors, no blank screens
  • All three GDPR webhook endpoints return 200 when called with test payloads (use the Shopify CLI's webhook deliver command)
  • No more than one primary Button per page section
  • Billing charge declined and approved flows both tested manually with a test store and test charges
  • App listing description matches actual functionality — no promises the app can't keep

What the rejection notice actually looks like

When your app is rejected, you receive an email from Shopify's Partner team. The email contains a list of numbered issues, each with a category (Technical Requirements, App Listing, Design Guidelines, Security), a short description of the problem, and a link to the relevant section of the developer documentation. The tone is professional and specific — it's not a vague 'your app needs improvement' but 'your app's Settings page has three primary buttons, which violates the Polaris button usage guidelines linked here.'

After receiving a rejection, you have access to a Partner dashboard thread where you can reply with questions or clarifications before resubmitting. Use this channel if a rejection point is ambiguous. For example, if the reviewer says 'the app does not handle the case where no products exist' and you're not sure which page they mean, ask. Getting clarity before resubmitting saves a full review cycle.

The scope audit: a practical workflow

Given that excess scopes are the most common rejection reason, here's a concrete approach to audit them. First, list every scope in your shopify.app.toml. Then, for each scope, search your entire codebase (including Remix loaders, actions, API routes, and webhook handlers) for the API calls that require that scope. Use the Shopify API scope reference at shopify.dev to cross-reference each mutation and resource with the scopes it needs.

For example: write_orders is required by orderUpdate, draftOrderCreate, and fulfillmentCreate. If your app calls none of these mutations — maybe it only reads order data to display a dashboard — you don't need write_orders, only read_orders. Do this exercise for every scope in your list. The result will almost always be a shorter scopes list than you started with.

One nuance: if you're building a feature in your next release that will require an additional scope, don't add the scope speculatively before that feature is live. Add scopes only when the code that uses them is in the submitted version. Future features requiring new scopes will need a re-review anyway, so adding them early doesn't save a review cycle — it just adds an unexplained scope that can cause a rejection today.

Senior Shopify Engineer

Frances Chen

Keep reading