stripe-dashboard
Operate your own Stripe account(s) — via the API where it works and a logged-in browser where it does not. Use for "mint a Stripe key", "update the branding/logo/icon on Stripe", "change the statement
Files1
View on GitHubSKILL.mdskills/stripe-dashboard/SKILL.md
GitHubStripe: API first, dashboard for the rest
Rule 1 — split the work by what the API can touch
The API can: products, prices, payment links (create/update/deactivate, incl.
invoice_creation, payment_method_types, custom fields, submit_type=donate), checkout
sessions, customers, invoices, files (files.stripe.com/v1/files, purpose
business_logo / business_icon), reading /v1/account.
The API cannot: write your own account. POST /v1/account returns 403 "you may only
use it on connected accounts" even with a full key. That covers branding (logo, icon,
colours), public details (name, support email/URL, website), statement descriptor,
dashboard timezone, and the customer-email toggles. All of that is the dashboard, in a
browser session. Do every API step first, then one browser pass per page.
Verify every dashboard write by reading /v1/account back — the page says "saved" for
things it did not save (e.g. a Save click that landed after a fullPage screenshot resized
the viewport).
Rule 2 — the verification step is passable, but it is the owner's
Creating a secret key, or changing identity-class fields, opens "Verification
required". A passkey prompt is the owner's to complete. The email route is: Stripe mails
"Your Stripe verification link" — open it in the same browser instance that holds the
session (a second launch with the same --user-data-dir hands the URL to the running
instance; curl proves nothing) — then Stripe texts a 6-digit code to the account phone.
In an OTP widget, click the first box and keyboard.press("Digit<n>") per digit; setting
.value fails.
If you script it, run the whole wizard as one browser-automation script (one connect per
launch) and have it poll a code.txt so a human or another tool can supply the code.
Restricted key of choice: "Full access — except sensitive operations" (no payouts or
transfers).
Rule 3 — dashboard mechanics that actually work
- Modals are
role="alertdialog", and the Setup-guide widget is a visiblerole="dialog"that will be the.pop()of any dialog query. Scope to the alertdialog, or search the whole document for a visible button by text. - Buttons are often
<a>or aPressablediv. Find by trimmed innerText acrossbutton,a,[role=button],scrollIntoView, thenmouse.clickat the centre. - The one
input[type=text]you find may be the global Search box. Scope byname(business_profile[support_email],settings[payments][statement_descriptor]) and set via the native value setter +input+change+blur. - Branding page (
/acct_.../settings/branding):[data-testid="branding-icon-upload"]andbranding-logo-upload. An empty slot is a hiddeninput[type=file]->uploadFile. A filled slot has no chooser: click the slot, then its top-right corner (x+w-2, y+2) — the × — and a fresh file input appears. Colour inputs are the text inputs whose value equals the current hex without#. Then the visible "Save changes" button. - Public details (
/settings/business-details-> "Customer-facing information" -> Edit): fields byname; the Save is a hidden submit —requestSubmit()on the form of any field saves it. Same page, "Statement descriptor" -> Edit. - Timezone (
/settings/account): abutton[role=combobox]under "Time zone"; click, type the city, click the[role=option], Save. - Customer emails (
/settings/emails): "Successful payments" and "Refunds" toggles auto-save on click and are often OFF by default — check them. Re-read on a fresh launch, notpage.reload(networkidle never settles on the dashboard). - Take viewport screenshots only.
fullPage:truechanges the layout and every coordinate after it.
Rule 4 — link hygiene
- A payment link printed on paper (a mailer QR code) is never deactivated. Grep your
print assets for
https://buy\.stripe\.com/\w+before any cleanup. - A link whose after-payment page now redirects somewhere unrelated is deactivated: a paid purchase that lands on a homepage is a broken promise.
- Every live link gets
invoice_creation[enabled]=truewith a description and a footer carrying the support address,customer_creation=always, andpayment_method_types[]=card,link— buy-now-pay-later on a small digital product is noise the buyer pays for in attention. - Checkout Sessions carry the same flags; test a change by creating a live session with the
new params and expiring it (
/v1/checkout/sessions/{id}/expire) — no charge, real validation. - Register every checkout host as a payment method domain, or embedded Checkout silently
hides Apple Pay and Google Pay:
GET /v1/payment_method_domains, andPOSTwithdomain_name=<host>if missing (free, instant).
Rule 5 — leave the machine clean
Kill any automated browser and delete the copied profile (it holds live session cookies);
delete any scratch file holding a key. A key's only homes are your .env and your host's
environment variables (to rotate on Vercel: delete the old env id, add the new one, then
redeploy).