Stripe Apps
StripeOfficialPublished by Stripe
- Skill
- Tool integration
- Software development
- MIT
- No issues found
AutoGineer summary
Written by AutoGineer from the skill’s own files, not by the author
This skill provides a structured approach to developing and maintaining Stripe Apps. It guides users through selecting the correct extension architecture, implementing authentication, building UI components, and managing backend services or webhooks. By leveraging bundled reference files and canonical documentation, it ensures that app development follows Stripe's best practices for security, configuration, and lifecycle management.
What it does
- Provides guidance on selecting the appropriate Stripe extension interface.
- Assists in building and modifying Stripe Dashboard UI components.
- Supports the implementation of backend services, webhooks, and secret storage.
- Helps determine the correct authentication method for Stripe API access.
- Offers instructions for testing, debugging, and publishing Stripe Apps.
- Clarifies the applicability of extensions for specific billing or workflow requirements.
When to use it
- When you need to build a custom UI extension for the Stripe Dashboard.
- When deciding between Restricted API keys, Platform keys, or OAuth for your app.
- When implementing backend logic or handling Stripe events via webhooks.
- When you need to store sensitive credentials securely using the Secret Store API.
- When troubleshooting or modifying an existing Stripe App's manifest or configuration.
Advantages
- Ensures alignment with official Stripe development patterns and security standards.
- Reduces development time by providing clear paths to relevant documentation.
- Helps prevent common architectural errors in app authentication and event handling.
- Provides a consistent workflow for the entire app lifecycle from scaffolding to release.
Usage examples
- “How do I determine if my Stripe App needs a backend?”
- The agent will reference the backend documentation to explain that a backend is required for long-term data storage, server-side secrets, or handling webhooks, while suggesting the Secret Store API for simpler needs.
- “Help me choose the right authentication type for a private Stripe App.”
- The agent will compare Restricted API keys, Platform keys, and OAuth, recommending Restricted API keys as the simplest and most secure choice for private integrations.
Install
Download the skill as a ZIP:
https://autogineer.com/agent-skills/stripe/stripe-apps/downloadUnzip it into the cross-client skills folder of your project:
mkdir -p .agents/skills && unzip stripe-apps.zip -d .agents/skills
Documentation by Stripe
Stripe Apps
Select and read the existing guidance for the user’s task. Product documentation describes what an extension can do; implementation guides describe how to build it. Before recommending an extension or writing code, load the applicable bundled reference files as well as the relevant product documentation. A link or filename in this skill isn’t a substitute for reading its contents.
For a capability question, check native Stripe features and the documented applicability and limitations of the extension. For an implementation request, create or modify working files using the selected guide. For an existing app, inspect its manifest, packages, installed SDK, and local agent instructions before deciding what to change.
Choose the relevant documentation
Use information already supplied by the user or existing app. Read <references/discovery.md> when you need to clarify the app’s requirements, and <references/extension-types.md> for an overview of app architectures. Ask only for missing information that affects the task.
Product capabilities
Use these existing product documents to determine which capability fits and whether the account has the required access. Don’t infer support from an extension’s name or the existence of a scaffold.
| Task | Read |
|---|---|
| Select an extension interface and its supported implementation types | Extension points and how extensions work |
| Change how items are included on subscription invoices | Item handling extensions |
| Customize prorations | Native proration behavior and prorations extensions |
| Apply customer balances | Customer balance extensions |
| Calculate discount amounts | Discount calculation extensions |
| Add an action to Stripe Workflows | Existing workflow actions, existing custom actions, and how custom actions work |
| Customize another product’s behavior | Follow the selected interface’s product documentation from the extension-point catalog |
Implementation guidance
The references/*.md files are bundled with this skill. After selecting the capability, load the applicable files before following their links to supporting documentation. Combine rows when an app needs multiple types. Follow the selected guide’s prerequisites, scaffolding, testing, and debugging instructions.
| Task | Read |
|---|---|
| Build or modify any Dashboard UI, including drawers and full-page apps | <references/ui-extensions.md>, then each selected component’s current API documentation |
| Author, test, or debug a script extension | <references/script-extensions.md>, then the selected extension point’s implementation guide |
| Build a custom-action script | <references/script-extensions.md> and build a custom action with a script |
| Build a custom-action remote function | Build a custom action with a remote function |
| Build a self-hosted UI back-end or webhook service | <references/backend.md> and <references/authentication.md>; also load <references/webhooks.md> when receiving Stripe events |
| Store app credentials with the Secret Store API | The Secret Store API section in <references/backend.md> and store secrets, including for apps without a self-hosted back-end |
| Look up APIs, SDK patterns, configuration, or additional extension documentation | <references/canonical-docs.md> |
Use the UI reference for layout and composition, and current component documentation for import paths, props, and styling APIs. For scripts and remote functions, follow the selected extension’s documentation; UI and self-hosted back-end instructions don’t define their runtime capabilities.
Shared app lifecycle
Read these references when the corresponding task is needed. Reuse existing apps and packages; scaffold only what is missing.
| Task | Read |
|---|---|
| Scaffold, build, test, preview, or upload an app | <references/workflow.md>, together with the selected implementation guide |
| Choose authentication for access to Stripe APIs | <references/authentication.md> |
| Receive Stripe events | <references/webhooks.md> |
| Build a first-run setup experience | <references/onboarding-ux.md> and the UI reference |
| Release versions, change installed permissions, or publish to the marketplace | <references/publishing.md> |
| Submit feedback after running app toolchain commands | <references/feedback.md> |
Load bundled reference files
Use the task tables to select every applicable bundled reference under
references/, then open those files with a file-reading tool before starting the corresponding work. For example, UI with a self-hosted back-end needs both UI and back-end guidance; onboarding also needs the UI reference.Follow relevant pointers to other bundled references inside the files you load, including filenames written as code rather than links. For example, back-end guidance can require authentication guidance, and changing permissions can require publishing guidance. Use this skill’s direct links to locate those files.
Read the canonical documentation required by those references. External documentation supplements the bundled guidance on workflow, layout, and other task-specific decisions.
Resolve
references/paths relative to this skill’s directory and bare reference filenames relative to itsreferences/directory. When reading the hosted skill, fetch the corresponding reference URLs relative to the skill URL.Load only references relevant to the task, and reuse contents already read during the session unless they have changed. A full-stack app doesn’t require every document in the skill.
Read canonical pages using the available documentation tools. Prefer
stripe docswhen available, including for pages that require account access.Use current product documentation and the installed SDK’s types for API contracts and limitations. If an example in a reference disagrees, check the version and follow the applicable canonical documentation.
Read any interface-specific agent instructions supplied with an existing or newly generated extension. If none are present, use the linked product and implementation guides.
If the documentation doesn’t establish that an extension meets a requirement, state what is unknown or unsupported before proceeding.
Keep the work within the user’s request. Report the changes, validation results, and remaining steps relevant to that app.
Files
- ATTRIBUTION.mdAdded by AutoGineer819 B
- LICENSELicence file added by AutoGineer1.1 KiB
- SKILL.md7.6 KiB
- references/authentication.md4.6 KiB
- references/backend.md4.4 KiB
- references/canonical-docs.md4.4 KiB
- references/discovery.md9.2 KiB
- references/extension-types.md5.1 KiB
- references/feedback.md1.9 KiB
- references/onboarding-ux.md2.7 KiB
- references/publishing.md5.7 KiB
- references/script-extensions.md7.3 KiB
- references/ui-extensions.md20.7 KiB
- references/webhooks.md3.4 KiB
- references/workflow.md8.3 KiB
references/ui-extensions.md
UI extensions — layout and craft
UI extensions
UI extensions render custom UI inside the Stripe Dashboard, in a sandboxed iframe. This skill is the opinionated layout-and-craft layer: how to compose a full-page or drawer app so it feels native — placement, composition order, spacing, density, states, typography. It does not restate component APIs; those live on each component’s doc page, and they’re the source of truth.
How to use this skill (read first)
Component API → fetch the component’s doc BEFORE you import it (required). SDK components are split across three import subpaths —
@stripe/ui-extension-sdk/ui,/ui/next, and/ui/experimental— and importing from the wrong one yields anundefinedcomponent and a hard crash (Element type is invalid). You can’t tell a component’s subpath from its name — for exampleDataTableandDetailPageare under/ui/experimentaland the charts under/ui/next, not the/uiyou’d expect. So for every component you use: (1) fetch its doc —https://docs.stripe.com/stripe-apps/components/<name>.md(append?app-sdk-version=9NextforTabs,LineChart,BarChart; discover components from the index); (2) copy the exact import line and required props / data shape char-for-char; (3) re-check every import against the doc before you finish. Don’t infer an API from the component name — a wrong import path, prop, or data shape is a hard runtime error and the #1 reason these apps don’t render.Layout, styles, composition, and states → follow the codified rules here (§2–§3). These are Stripe’s craft defaults; no single component doc covers them. This is what the skill adds on top of the docs.
Constraints — the sandbox (these cause silent failures or crashes)
UI extensions run in a sandboxed iframe on React 17.0.2. Only SDK components render. Don’t reach for these:
| Blocked | Use instead |
|---|---|
Any HTML tag (<div>, <span>, <button>, <input>, <form>, <h1>…) | SDK components only (Box, Button, TextField, …) |
| CSS / Tailwind / MUI / styled-components / any stylesheet | the css prop with design tokens (§3) |
React 18+ APIs — useId, useTransition, useDeferredValue, concurrent features | React 17 hooks only (Stripe Apps run React 17.0.2) |
window, document, localStorage, sessionStorage | not available in the iframe |
react-hook-form / any ref-based form library | uncontrolled inputs — defaultValue + onChange (see Forms, §3) |
arbitrary fetch() to external URLs | fetchStripeSignature for your backend; the SDK client for Stripe APIs |
Data access (for apps that read Stripe data — the examples here use mock data). Initialize the client with createHttpClient from @stripe/ui-extension-sdk/http_client plus the STRIPE_API_KEY constant (a sentinel, not a real key — it uses the app’s granted permissions), then call standard SDK methods. Every resource you call must be declared as a permission (stripe apps grant permission …) or the request fails with an invalid-request error. The current object is environment.objectContext (for example, .id = "cus_…"); the signed-in user is userContext, a top-level prop — not nested under environment. Full rules: how UI extensions work · Extensions SDK API reference.
1. Placement — pick your viewport
Decide where in the Dashboard the app lives; that determines the viewport and the root component. Full viewport list: viewports reference.
| Your goal | Surface | Viewport | Root component |
|---|---|---|---|
| A dedicated workspace: tabs, lists, dashboards, multi-step workflows | Full-page | stripe.dashboard.fullpage | FullPageView |
| Contextual info/actions tied to a specific object (such as a customer or payment) | Page-specific | stripe.dashboard.{object_type}.detail, stripe.dashboard.{object_type}.list, or stripe.dashboard.{object_type}.overview | ContextView |
| Available on every Dashboard page | Dashboard-wide drawer | stripe.dashboard.drawer.default | ContextView |
| App configuration | Settings | settings | SettingsView |
| First-run setup after install | Onboarding | onboarding | OnboardingView |
Rules of thumb: lead with full-page when the app is a destination with more than one section; use a page-specific drawer when the value is glanceable context on an existing object; only use drawer.default when the app truly applies everywhere. A full-page app can also register drawer/page-specific views — link between them.
2. Composition — the build order
The order and which component does which job (the API of each is in its linked doc).
Full-page app (walkthrough: full-page apps pattern):
Manifest — register the
stripe.dashboard.fullpageviewport → your view. (The CLI’sadd viewadds a full-page view to an existing app; the full-page view needs@stripe/ui-extension-sdk≥ 9.2.)Shell — FullPageView; the header (app name + icon) comes from
stripe-app.json. Add onepageActiononly if there’s a single clear top-level action.Routing —
createRoutes+AppRouter; read the route withuseAppRoute, navigate withuseNavigation. Use a/:tabId?pattern so tabs are bookmarkable (routing).Tabs — Tabs/Tab for top-level sections. Distinct areas only; don’t nest tabs.
Overview — OverviewPage with a
primaryColumn(main content, charts) and asecondaryColumn(supporting modules). Group content intoPageModules with titles; lead with a summary. (See the OverviewPage doc for the exact column/PageModuleparent-child contract.)List — DataTable: sortable columns, status cells, row → detail route, pagination, and an empty state.
Detail — DetailPage with
breadcrumbsback to the list and two columns. The tab bar isn’t visible here; the breadcrumb is the way back.Create / edit — FocusView drawer over the current view.
Drawer / page-specific app: root is ContextView; keep it single-column and dense (a drawer is narrow — don’t force multi-column). Use environment.objectContext for the current object. If you also have a full-page experience, link out to it rather than cramming a workflow into the drawer.
3. Layout and style rules (the codified craft)
These are the defaults that make an app feel native — they’re not in any single component doc, so follow them here. Each is tagged [Required] (breaks/looks wrong otherwise), [Recommended] (Stripe’s craft default), or [Optional] (a style choice). Full styling reference: style your app.
[Required] css values are tokens, not web CSS. The css prop isn’t CSS. Every value is a design token or fraction, never a raw unit:
Spacing (
padding,margin,gap) → tokens only (xxsmall…xxlarge). Never"24px","1rem",%.Layout →
stack: "x" | "y"withgap. There is nodisplay: "flex"/"grid".Width → a fraction (
"1/2","1/3", …) or"fill". Height → a bare number for pixels (for example,height: 180).Color/background → semantic tokens (
backgroundColor: "surface" | "container",color: "secondary"), not hex.
Passing a raw CSS value (px, flex, hex) is a hard runtime error — the #1 way a naive build crashes. (style reference)
[Recommended] Spacing — Stripe’s token scale, tighter = more related. Spacing (padding/margin/gap) uses Stripe’s fixed token scale — match these defaults, never raw px. Use the smallest gap that still separates things:
| Token (value) | Default use |
|---|---|
xxsmall (2px) | label → its value; tightest intra-element spacing |
xsmall (4px) | icon → adjacent text; spacing inside a chip/badge |
small (8px) | between sibling cards/tiles in a row |
medium (16px) | padding inside a card/module; between fields in a column |
large (24px) | between distinct sections of a page |
xlarge (32px) | between the two major columns of a layout |
xxlarge (48px) | rarely — a major page break |
[Recommended] Content aligns to the tab’s left edge — no wrapper padding. The Tabs bar and FullPageView already set the page’s content edge. Don’t wrap a tab’s panel content in a Box with padding (or paddingX/paddingLeft) — that inset pushes content off the tab’s left edge and breaks alignment with the tab labels above it. Use stack: "y" + gap for vertical rhythm between modules instead; content stays flush to the same left edge as the first tab.
// Incorrect — inset; content no longer aligns to the tabs
<Box css={{ stack: "y", gap: "large", padding: "large" }}>…</Box>
// Correct — flush to the tab's left edge
<Box css={{ stack: "y", gap: "large" }}>…</Box>[Recommended] Page structure — one consistent column layout, OverviewPage rendered directly. Render OverviewPage directly as the tab’s content — not wrapped in a Box, and never with a full-width band stacked above it. OverviewPage is the layout; pick its shape by whether you pass secondaryColumn:
One column →
primaryColumnonly (renders full-width).Two column →
primaryColumn+secondaryColumn. Never mix the two — no full-width KPI row or band above a two-column split. The KPI stat row is the firstPageModuleofprimaryColumn(full-width in one-column mode, primary-column width in two-column mode), not a separate row above the component. Group every module into the columns; don’t build a manual column layout.
[Required] DetailPage is its own root route — never inside FullPageView. A detail is a separate route you navigate to (for example, route("/members/:memberId", …)) that renders DetailPage at the root. DetailPage owns its page shell; nesting it in FullPageView double-stacks the header. The breadcrumb — not the tab bar — is the way back.
[Recommended] Overview composition & density — fill the page. An overview must read as a dense, width-filling dashboard, not a short column of big cards. This is the #1 thing that makes an overview look un-native, so compose it deliberately:
Top: a horizontal KPI stat row — 3–5 equal tiles side by side (see Stat tiles). Never stack KPI cards vertically full-width (one metric per row) — a column of oversized single-metric cards wastes the page and reads as un-native.
Below: use both columns. With
OverviewPage, put the primary module (a trendLineChart, or the main list/table) inprimaryColumnand supporting modules insecondaryColumn; otherwise split withstack: "x", gap: "xlarge"into a wider left (width: "2/3") and a narrower right (width: "1/3"). Don’t leave half the width empty.Derive enough views to fill it. If the data is only a few metrics, add the breakdowns, trends, top-N lists, and recent-activity the data implies (for example, points-over-time trend, members-by-tier breakdown, top members, recent redemptions) rather than leaving whitespace. Aim for 3+ modules that fill the viewport.
Avoid: a single column of oversized full-width cards; a large empty right side or lower page; one metric per row. Match the density of a native Dashboard overview.
[Recommended] List pages — the table is the hero. A dedicated list/directory page (for example, a Members tab) is the table itself, full-width, as the primary content. The only things around it: search / filters (and segment tabs) above the table, pagination below, and an empty state. Don’t put KPI stat tiles, charts, or dashboard modules on a list page — those belong on the overview. A native list page is dense with rows, not decorated with summary cards on top. Keep it: controls → full-width table (many rows) → pagination. (Overviews are multi-module and dense; list pages are single-purpose and focused — don’t blur the two.)
[Recommended] Stat tiles — a row of top-line KPI cards. A single row of equal surface cards (not a 2×2 grid), each a muted caption label above a large semibold value — use the card treatment from Cards & trays. Lay them out as a horizontal row with equal widths:
// row wrapper: <Box css={{ stack: "x", gap: "medium" }}> … one card per KPI …
<Box css={{ width: "fill", stack: "y", gap: "xxsmall", padding: "medium", borderRadius: "medium", backgroundColor: "surface" }}>
<Inline css={{ font: "caption", color: "secondary" }}>{label}</Inline>
<Inline css={{ font: "subtitle", fontWeight: "semibold" }}>{value}</Inline>
</Box>Aim for ~3–5 cards in one row (for example, Total spend · MRR · Refunds · Disputes). For proportional data (a total split into parts), prefer a progress/MeterChart treatment over a chart — see Charts.
[Recommended] Two-column detail (key-value). Outer stack: "x", gap: "xlarge"; each column width: "1/2", stack: "y", gap: "medium"; each field stack: "y", gap: "xxsmall" with a semibold label above a regular value.
[Recommended] Charts & data viz — pick the representation that fits the data.
Sizing: a chart needs an explicit height — wrap it in a Box with a pixel height (
~180per the chart-layout pattern) inside aPageModule.Trend over time → LineChart. Use a sensible granularity (monthly or weekly); daily points over a long range render as an unreadable, noisy line.
A small breakdown / a total split into parts (for example, members-by-tier) → a
Listof rows (or a MeterChart for a proportional bar). ABarChartwith only a few categories renders as a lonely narrow bar in an empty module — so use a list:
import { List, ListItem, Inline } from "@stripe/ui-extension-sdk/ui";
<List>
{tiers.map((t) => (
<ListItem key={t.name} id={t.name} title={<Inline>{t.name}</Inline>} value={<Inline>{`${t.count} members`}</Inline>} />
))}
</List>Reserve BarChart for genuine multi-bar / time-series data, and let it fill width.
Read the component’s doc for the exact
datashape before wiring — charts are strict (wrong shape = hard runtime error).[Optional] a
surface/containerbackground makes a chart read as a card; not required.
[Recommended] Typography. font accepts only these presets — don’t invent values ("heading4", "title2", and similar are not valid and crash): body, bodyEmphasized, caption, heading, subheading, subtitle, title, kicker, lead. fontWeight accepts only regular | semibold | bold. For emphasis use fontWeight: "semibold"; use regular for body. Don’t use fontWeight: "bold" (the SDK accepts it, but Stripe’s design language reserves it — semibold is the native emphasis weight). Labels are font: "caption" + color: "secondary". (style reference)
[Recommended] Cards & trays — a background implies a radius. When a Box should read as a card or tray, set surface and radius together: a card = backgroundColor: "surface" + borderRadius: "medium" + padding: "medium"; group related cards on a tray = backgroundColor: "container" + borderRadius: "medium" + padding: "small". borderRadius accepts none | xsmall | small | medium | large | rounded; medium is the card default. A plain layout Box that isn’t a card gets no background or radius.
[Recommended] Loading. Put the loading state inside the tab/content region so the header and tab bar stay visible — don’t wrap Tabs or the whole view in a loading state. Center a Spinner (loading pattern).
[Recommended] Empty states. Give DataTable an empty state, and swap it by scenario: an object with a call to action when there’s genuinely no data; a plain string when active filters produce zero results (empty-state pattern).
[Required] Forms are uncontrolled. There is no react-hook-form or ref-based forms in the sandbox. Use uncontrolled inputs — defaultValue + onChange (or a plain React-17 useState controlled value) — for TextField, Select, and similar. A ref-based form library won’t work.
4. Component index
The complete, authoritative catalog is docs.stripe.com/stripe-apps/components — every component, grouped by Views · Layout · Navigation · Content · Forms · Charts. Start there to find the right component for anything not covered below (there are ~40; the table here is a curated shortcut for the common full-page jobs, not exhaustive). Then open that component’s own doc for its API. Pick by the job; read the doc for the API (props, data shape, allowed parents/children).
| Job | Component | When to use | Doc |
|---|---|---|---|
| Root of a full-page app | FullPageView | Full-page viewport; header from manifest | doc |
| Root of a drawer / page-specific view | ContextView | Narrow, single-column, dense | doc |
| Top-level sections | Tabs / Tab (ui/next) | Distinct workflow areas; route-driven | doc |
| Overview dashboard | OverviewPage + PageModule | Two-column summary; group content in modules | doc |
| List of objects | DataTable | Sortable, status cells, row→detail, empty state, pagination | doc |
| Single object detail | DetailPage (+ PropertyList for key-value) | Breadcrumb + two columns; top-level page, not inside FullPageView | detail · propertylist |
| Create / edit | FocusView | Overlay drawer; Button pending on save | doc |
| Data visualization | LineChart / BarChart / MeterChart / Sparkline (ui/next) | In a fixed-height Box in a PageModule; read the doc for the data shape | line · bar |
| Layout / spacing | Box, Inline | The stack/gap/padding substrate (see §3) | doc |
| Actions | Button | Primary/secondary; pending for async | doc |
| Loading | Spinner | Center in the content region | doc |
Full catalog: all components · design patterns
Requirements and compatibility
Works with: Claude Code, Claude apps, OpenAI Codex, GitHub Copilot, Cursor, Gemini CLI, Kiro, OpenCode, Goose, Amp, Roo Code, JetBrains Junie, Factory Droid, Devin (ex-Windsurf), Mistral Vibe, OpenHands, Letta
“Works with” means the tool reads the open Agent Skills format; a skill written for one tool may use features others lack.
Security
No issues found by our static scan· No issues found
A static scan is not a guarantee. Skills are third-party content: review a skill before you let an agent use it.
Attribution and licence
- Published by
- Stripe
- Publisher
- Stripe (Official)
- Source
- stripe/ai · skills/stripe-apps · commit 38fa045
- Licence
- MIT License (redistribution allowed)
- LICENSE (added by AutoGineer from LICENSE)
- ATTRIBUTION.md (written by AutoGineer)
- Synced
- Also distributed in
- stripe/ai, stripe/ai, stripe/ai, stripe/ai, stripe/ai
AutoGineer is not affiliated with or endorsed by Stripe or Stripe.
Download ZIP
The ZIP holds the skill’s files, its licence files and an ATTRIBUTION.md. Download stripe-apps.zip