App Review Quality Requirements

Stripe reviews every app submitted to the marketplace against a comprehensive set of quality requirements. Understanding these standards before development saves time and reduces the number of review iterations.

Overview

The Stripe App review evaluates your app across six key areas:

  1. Transparent Pricing — Clear communication of costs
  2. App Functionality — Reliability and completeness
  3. Developer Standards — Code quality and API usage
  4. UX Quality — User interface and experience standards
  5. Security — Data protection and secure practices
  6. Legal Compliance — Privacy and regulatory requirements

Transparent Pricing

Your app must clearly communicate all costs to users:

  • Pricing disclosure: All pricing must be stated upfront in the marketplace listing
  • No hidden fees: Users must not encounter unexpected charges after installation
  • Trial terms: If offering a trial, clearly state the duration and what happens after it ends
  • Upgrade flows: Any upsell or upgrade prompts must be non-intrusive and clearly optional
  • Currency: Display prices in the user’s local currency when possible

Caution

Apps that obscure pricing or charge users without clear consent will be rejected immediately.

Date and Time Formatting

All dates and times displayed in your app must follow Stripe Dashboard conventions:

  • Use the user’s locale for date formatting when available
  • Display times in the user’s local timezone
  • Use relative timestamps for recent events (e.g., “2 hours ago”)
  • Use absolute timestamps for older events with full date and time
  • Follow ISO 8601 for any API-facing date fields
// Good: Use Stripe's date formatting utilities
import { formatDate, formatRelativeTime } from '@stripe/ui-extension-sdk/utils';
const formattedDate = formatDate(timestamp); // Locale-aware
const relativeTime = formatRelativeTime(timestamp); // "2 hours ago"

App Settings

If your app requires configuration:

  • Provide a dedicated Settings view accessible from the app’s viewport
  • Pre-populate sensible defaults where possible
  • Validate all user inputs with clear error messages
  • Allow users to update settings without reinstalling the app
  • Persist settings across sessions using the Stripe Secret Store API

Sandbox Support

Your app must work correctly in Stripe’s sandbox (test) mode:

  • Test mode compatibility: All features must function in test mode
  • Test data: Use realistic test data that demonstrates app functionality
  • No live data in sandbox: Never expose production data in test mode
  • Graceful handling: If a feature is unavailable in sandbox, display a clear message explaining why
  • Set sandbox_install_compatible: true in your manifest
{
"sandbox_install_compatible": true
}

App Functionality

Reliability

  • The app must not crash or freeze during normal use
  • All advertised features must work as described
  • Network errors must be handled gracefully with retry options
  • The app must remain responsive during background operations

Completeness

  • No placeholder content, “coming soon” features, or broken links
  • All UI elements must be functional — no dead buttons or inactive controls
  • Help text and documentation links must resolve to valid pages
  • Uninstallation must cleanly remove all app data and webhooks

Performance

  • UI must render within 3 seconds on a standard connection
  • Background sync operations must not block the UI
  • Large data sets must use pagination or lazy loading
  • Minimize API calls to avoid rate limiting

Developer Standards

API Usage

  • Use the latest stable version of the Stripe API
  • Follow Stripe API best practices for pagination, error handling, and idempotency
  • Do not exceed rate limits — implement exponential backoff for retries
  • Use webhooks for event-driven updates instead of polling

Code Quality

  • No console errors or warnings in production builds
  • Remove all debug logging before submission
  • Handle all edge cases (empty states, missing data, network failures)
  • Follow Stripe’s component library patterns for consistent UI

Versioning

  • Use semantic versioning (MAJOR.MINOR.PATCH)
  • Document breaking changes in version updates
  • Maintain backward compatibility where possible

UX Quality

Advertising

  • No ads: Your app must not display advertisements of any kind
  • No cross-promotion: Do not promote other products or services within the app UI
  • Branded content: Only display your own brand identity, not third-party brands (except integration partners like Brevo)

Language and Content

  • Consistent language: Use consistent terminology throughout the app
  • Professional tone: Match the Stripe Dashboard’s professional, concise communication style
  • No jargon: Avoid technical jargon that merchants may not understand
  • Spell check: Ensure all text is free of spelling and grammatical errors
  • English only: All user-facing text must be in English for marketplace apps

Confirmation Dialogs

Require user confirmation for destructive or significant actions:

// Good: Confirm before destructive actions
const handleDisconnect = async () => {
const confirmed = await showConfirmation({
title: 'Disconnect Brevo Integration',
message: 'This will stop syncing customer data to Brevo. You can reconnect at any time.',
confirmLabel: 'Disconnect',
cancelLabel: 'Cancel',
destructive: true,
});
if (confirmed) {
await disconnectIntegration();
}
};

Actions that require confirmation:

  • Disconnecting integrations
  • Deleting synced data
  • Changing settings that affect data flow
  • Resetting configuration to defaults

Loading States

Always show loading indicators for asynchronous operations:

// Good: Show loading state during data fetch
const CustomerSyncStatus = () => {
const { data, isLoading, error } = useSyncStatus();
if (isLoading) {
return <Spinner label="Loading sync status..." />;
}
if (error) {
return <Banner type="critical" title="Failed to load sync status">
{error.message}
</Banner>;
}
return <SyncStatusDisplay data={data} />;
};

Requirements:

  • Show spinners or skeleton screens during data loading
  • Disable buttons during form submission
  • Display progress indicators for long-running operations
  • Never show a blank screen while loading

Error Messages

Provide clear, actionable error messages:

// Bad: Generic error
"Something went wrong"
// Good: Specific and actionable
"Unable to sync customer data to Brevo. Please verify your Brevo API key
in Settings and try again."

Error message guidelines:

  • Explain what happened in plain language
  • Suggest a specific action the user can take to resolve the issue
  • Provide a way to retry the failed operation
  • Log detailed error information for debugging (not shown to users)
  • Include error codes for support reference when applicable

Security

Secret Store API

Use Stripe’s Secret Store API for all sensitive data storage:

import { createHttpClient, STRIPE_API_KEY } from '@stripe/ui-extension-sdk/http_client';
// Good: Store secrets using the Secret Store API
const storeBrevoApiKey = async (apiKey: string) => {
const stripe = createHttpClient(STRIPE_API_KEY);
await stripe.apps.secrets.create({
name: 'brevo_api_key',
payload: apiKey,
scope: { type: 'account' },
});
};
// Good: Retrieve secrets from the Secret Store
const getBrevoApiKey = async () => {
const stripe = createHttpClient(STRIPE_API_KEY);
const secret = await stripe.apps.secrets.find({
name: 'brevo_api_key',
scope: { type: 'account' },
});
return secret.payload;
};

Never store sensitive data in:

  • Local storage or session storage
  • Cookies
  • URL parameters
  • Hardcoded values in source code
  • Plain text configuration files

Cryptography

  • No custom cryptography: Do not implement your own encryption algorithms
  • Use Stripe’s built-in security primitives (Secret Store, signing secrets)
  • Use HTTPS for all external API calls
  • Validate all webhook signatures before processing

Data Handling

  • Only request permissions your app actually needs
  • Do not store Stripe data outside of what is necessary for functionality
  • Implement data retention policies aligned with your privacy policy
  • Provide a mechanism for users to request data deletion

Privacy Policy

Your app must have a publicly accessible privacy policy that covers:

  • What data your app collects from Stripe
  • How the data is stored, processed, and shared
  • Data retention and deletion policies
  • User rights regarding their data
  • Contact information for privacy inquiries
  • Compliance with applicable regulations (GDPR, CCPA, etc.)

Terms of Service

  • Provide clear terms of service for your app
  • Do not include terms that conflict with Stripe’s Terms of Service
  • Clearly state any usage limitations or restrictions

Regulatory Compliance

  • Comply with all applicable data protection regulations
  • Implement appropriate data processing agreements
  • Support data portability and deletion requests
  • Maintain audit logs for data access and processing

Review Process Timeline

StageDuration
Initial submission5-10 business days
Revision review3-7 business days
Final approval1-2 business days
PublicationImmediate after approval

Tip

Address all review feedback in a single revision to avoid multiple review cycles. The Stripe team provides specific, actionable feedback for each issue found.

Common Rejection Reasons

  1. Missing error handling — App crashes on network errors or unexpected data
  2. Insufficient loading states — Blank screens during data fetching
  3. Unclear pricing — Pricing not fully disclosed in the listing
  4. Excessive permissions — Requesting permissions not needed by the app
  5. Broken sandbox mode — App doesn’t function in test mode
  6. Security issues — Storing secrets outside the Secret Store API
  7. Missing privacy policy — No accessible privacy policy URL
  8. Incomplete functionality — “Coming soon” features or placeholder content
AI Assistant

Hi! Ask me anything about the docs.

Start Free with Brevo