gumroad CLI
SkillFiles & storageUse the `gumroad` CLI to look up and manage Gumroad data from the terminal. Trigger when the user asks about Gumroad products, files, file uploads, attachments, sales, subscribers, licenses, payouts, audience emails, email workflows, broadcasts, offer codes, webhooks, refund policies, or any Gumroad data lookup. Also trigger on "check my Gumroad", "look up a sale", "verify a license", "list my products", "how much have I made", "who bought", "recent sales", "refund a sale", "create a product", "upload a file", "attach a file to a product", "add a cover image", "set a product thumbnail", "get product content", "set product content", "upload product media", "publish a product landing page", "publish custom HTML", "clear custom HTML", "customize my profile page", "publish a profile landing page", "set profile custom HTML", "attach a file to a variant", "finish a failed upload", "abort an upload", "manage webhooks", "draft an email", "preview a broadcast", "send an audience email", "list drafts", "set refund policy", "check my refund policy", "check my earnings", "see my revenue", "who subscribed", "manage my store", "discount code", "coupon", "shipping status", "payout schedule", or any request to query or act on Gumroad data — even if the user doesn't say "Gumroad" explicitly but is clearly referring to their creator store or digital product sales. Do NOT trigger for Gumroad web UI, Rails, or codebase questions.
Available today. Use it from your connected AI after setup.
No other account needed.
Connect ahel once, and every AI you use reads what you have installed.
Then ask your AI: use the gumroad CLI skill
What this skill tells your AI
The instructions your AI receives, as published by antiwork/gumroad-cli in skills/gumroad/SKILL.md and read by ahel’s review.
Use gumroad (Gumroad CLI) to query and manage Gumroad data.
Agent invariants
Always follow these rules:
- Always pass
--no-inputto prevent interactive prompts from blocking. - Always pass
--jsonfor programmatic access. - Use
--json --jq <expr>together to extract exactly what you need. - For operations that can prompt for confirmation (delete, refund, workflow step adds, workflow delay changes, mutating admin actions,
files abort,files completereplay, product updates that remove files, orproducts content setwhen omitted page IDs will be deleted), add--yesto skip confirmation. - Pass
--quietto suppress spinners and status messages. - Pass
--dry-runto preview mutating requests without executing them. - Use
--page-delay 200mswith--allto avoid rate limits on large datasets. - Prices are in whole currency units (e.g.
--price 10.00for $10), not cents. The CLI converts internally. Use--currency eurto change currency. - Products are created as drafts — use
gumroad products publish <id>to make them live. - Product cover and thumbnail uploads support JPEG, PNG, and GIF. WebP is not supported by the API and the CLI rejects it before upload.
- Product custom HTML landing pages use
gumroad products page preview <id> ./landing.htmlto run the backend sanitizer without writing,gumroad products page publish <id> ./landing.htmlto store the page,gumroad products page clear <id> --yesto remove it, andgumroad products page url <id>to print the live URL.--dry-runonly previews the CLI request body; it does not call the backend sanitizer. Inspect.sanitization_reportinpreviewandpublishJSON output for server-side changes. - Profile custom HTML landing pages mirror the product commands without a product id and without checkout:
gumroad user page preview ./landing.html,gumroad user page publish ./landing.html(read from stdin with-),gumroad user page clear --yes, andgumroad user page url(prints the public profile URL and its/landing/embedURL). A profile has no buy button, so omitdata-gumroad-action="buy"and the checkout data attributes; link to products instead. - Storefront pages (slugged pages serving at
<username>.gumroad.com/<slug>) usegumroad pages list,gumroad pages create --title <title> [--slug <slug>] [path](with an HTML path or-the page is created as custom HTML; without one it starts empty for the in-app editor),gumroad pages pull <slug>to download a page's existing custom HTML so pull → edit → push is a real round trip (writes<slug>.html;-o <path>to choose,-o -for stdout; refuses to overwrite without--force; errors with a hint when the page has no custom HTML),gumroad pages scaffold <slug>to generate starter HTML for a rich-text page or a default profile from a static snapshot of its current render (same flags as pull; pushing the scaffold converts the page to custom HTML and replaces the dynamic storefront/editor experience),gumroad pages push <slug> ./page.htmlto replace a page with custom HTML, andgumroad pages preview ./page.htmlto run the backend sanitizer without publishing. The loop for going custom:pull(orscaffoldwhen there's no custom HTML yet) → edit →preview→push.--json/--jqonpull/scaffoldstill write the file and additionally print the raw API response; combining them with-o -is rejected since both would own stdout. The special slugprofiletargets the profile landing page (pull profiledownloads your published custom HTML,scaffold profilesnapshots the default storefront render when none is published; push uses the same endpoints asuser page publish). Custom HTML pages require the seller'scustom_html_pagesfeature to be enabled; without it writes fail with an access message. Writes to slugged pages (pages create/pages push <slug>) also require the token to carry theedit_profilescope — tokens minted before the CLI requested that scope get a 403 telling them to re-rungumroad auth login. - Product rich content uses
gumroad products content list <id> --json --no-inputto inspect page IDs,gumroad products content get <id> --json --no-inputto dump the sharedrich_contentpage array, andgumroad products content set <id> content.json --dry-run --json --no-inputto preview a whole-document replacement. Without an explicit path, whole-documentsetreads./content.json;set --pagereads./page.json. Use--page <page_id>withget/setto edit one matching page object;set --pagestill sends a merged whole-document PUT. For per-variant content, pass both--variant <variant_id>and--category <cat_id>. Whole-documentsetdeletes existing pages omitted from the JSON. - Custom HTML pages can use
data-gumroad-field="name",data-gumroad-field="price",data-gumroad-field="description", anddata-gumroad-action="buy". To preselect checkout state, adddata-gumroad-option="<variant name>",data-gumroad-quantity="<integer>",data-gumroad-price="<decimal>", ordata-gumroad-recurrence="monthly|quarterly|biannually|yearly|every_two_years". Production validates these values and falls back to product defaults when invalid. Prefer anchors for buy CTAs so production can add a checkout href; non-anchor buy elements also post to checkout. - Audience emails are created as drafts by default. Use
gumroad emails send-preview <id> --json --no-inputand inspect.preview_urlbeforegumroad emails send <id> --yes --json --no-input. Creating with--sendpublishes and blasts immediately, so use--dry-runfirst and require explicit human approval. To schedule a send instead of blasting immediately, create the draft thengumroad emails schedule <id> --at "<RFC3339>" --json --no-input;gumroad emails unschedule <id>returns a scheduled email to draft. - Workflow email writes do not change the workflow publication state. Adding a step to a published workflow can schedule eligible past recipients. Changing a delay can reschedule recipients. Use
--dry-runbefore each write. - If a command fails with a seller auth error, run
gumroad auth status --json --no-inputfirst. Agents can start seller auth withgumroad auth login --no-inputand hand the printed approval URL to a human, or use an existing seller token viaGUMROAD_ACCESS_TOKENorgumroad auth login --with-token. - For admin commands in agents/CI, pass
--non-interactiveand setGUMROAD_ADMIN_TOKEN; interactive shells can store an admin token withgumroad auth login --web.
Response shapes
Most responses are wrapped in {"success": true, ...} with resource-specific keys:
user→.user,user update→.useruser page preview→.custom_html,.sanitization_reportuser page publish/user page clear→.custom_html,.previous_custom_html,.profile_url,.sanitization_reportuser page url→.profile_url,.has_landing_pagepages list→.pages[](.slug,.title,.content,.custom_html,.url);pages create/pages push <slug>→.page;pages pull <slug>/pages scaffold <slug>→.page+.rendered_html(pull writes the page's.page.custom_html; scaffold writes.rendered_html, the static render snapshot);pages pull profile/pages scaffold profile→.custom_html,.rendered_html,.has_landing_page,.profile_url;pages push profile→ profile shape (.custom_html,.previous_custom_html,.profile_url,.sanitization_report);pages preview→.custom_html,.sanitization_reportrefund-policy view/set→.refund_policyproducts list→.products[]products view→.productproducts content get→ rich content page array directly, or one page object with--pageproducts content list→ rich content page summary array directlyproducts content set→ mutation envelope with.resultsales list→.sales[]sales buyers→.buyers[](email,name,purchase_count,last_purchase_date,utm_source,utm_medium,utm_campaign,utm_term,utm_content)sales view→.sale(includes.currency, the ISO code the sale is priced in — the same currency a refund amount is read in)sales export→.status,.recipient_emailsales summary→.gross_cents,.net_cents,.breakdown[]emails list→.emails[],emails view/create/send/schedule/unschedule→.email,emails send-preview→.preview_url,emails delete→.messageworkflows list→.workflows[],workflows view→.workflow,workflows add-email/update-email→.emailpayouts list→.payouts[],payouts view/upcoming→.payoutsubscribers list→.subscribers[],subscribers view→.subscriberlicenses verify→.purchaseoffer-codes list→.offer_codes[]upsells list→.upsells[],upsells view/create/update→.upsell,upsells delete→.messagevariant-categories list→.variant_categories[]variants list→.variants[]files upload/files complete→.file_urlmedia upload→.media(.id,.name,.url,.file_size),media list→.media[],media delete→.messageproducts createwith media flags →.productplus.media[]products updatewith media flags →.productplus.media[]products covers add --image→.result.covers[],.result.main_cover_id, plus.result.media[]products covers add --url→.result.covers[],.result.main_cover_idproducts thumbnail set --image→.result.thumbnail, plus.result.media[]products thumbnail set --url→.result.thumbnailproducts page preview→.custom_html,.sanitization_reportproducts page publish/products page clear→.product.custom_html,.product.landing_url,.previous_custom_html,.sanitization_reportproducts update --custom-html→.product.custom_html,.product.landing_url,.previous_custom_html,.sanitization_reportproducts create/update --refund-period/--refund-fine-printandproducts view→.product.refund_policy(.refund_periodisinheritornone/7/14/30/183, plus.title,.fine_print,.inherited)- Not every
productswrite verb is flat:create,update,unpublish, anddeletereturn top-level fields, butcovers add,thumbnail set, andcontent setstill wrap their payload in the{success, …, result}envelope — read those under.result webhooks list→.resource_subscriptions[]admin users info→.user(includes.user.stripeStripe Connect state —connected, and when connectedstripe_connect_account_id,stripe_dashboard_url, and averificationblock of flags/counts or anerrorsubfield when the live Stripe lookup failed — and.user.admin_linksimpersonate/user/purchases/stripe-dashboard URLs)admin users affiliates→.affiliates[]admin users comments list→.comments[]admin users comments add→.commentadmin users compliance→.compliance_info,.info_requests[]admin users credits add→.user_id,.credit.id,.credit.amount_cents,.credit.reason,.credit.crediting_user_id,.credit.created_atadmin users credits list→.credits[],.pagination.nextadmin users radar→.radar_stats,.recent_efws[]admin users purchases→.purchases[]admin users related→.related_users[],.truncated,.per_signal_limitadmin users mark-compliant,admin users suspend,admin users suspend-for-tos-violation→.status,.message,.user_idadmin products flag-for-tos-violation→.status,.message,.user_id,.product_idadmin payouts list→.recent_payouts[],.pagination.next. Each payout carriesstripe_transfer_id(apo_…payout orpy_…destination payment, or null),bank_account(null for PayPal and debit-card payouts; otherwisebank_numberrouting/BIC,account_holder_full_name,account_type,currency), andtrace_id(currently always null).admin payouts scheduled create→.message,.user_id,.scheduled_payoutadmin users refund-balance→.status,.message,.user_id,.count,.total_amount_cents,.currencyadmin users refund-all-for-fraud→.success,.user_id,.status(queued),.message,.purchases_to_refund,.block_buyersadmin purchases view→.purchaseadmin purchases search→.purchases[],.has_more,.limitadmin purchases lookup→.purchases[]admin products list→.products[],admin products view→.product
Admin pagination models differ by command:
- Cursor-paginated:
admin users affiliates,admin users comments list,admin users credits list,admin users radar,admin users purchases, andadmin purchases lookupreturn.pagination.nextas a cursor string. Pass it back with--cursor. - Page-paginated:
admin products listreturns.pagination.nextas an integer page number. Pass it back with--page; use--per-pagefor page size. - Capped, not continuable:
admin users relatedreturns at most 50 related users per signal. Always inspect.truncated; when any signal istrue, the result hit the cap and there is no cursor/page to fetch the rest. - Capped, not continuable:
admin purchases searchreturns.has_morewhen the server capped results.--limitis server-capped at 25 and there is no continuation token.
Bulk operations
When creating or updating many products:
- Check existing products and permalinks first, then skip duplicates on re-runs.
- Derive custom permalinks deterministically from source data so retries are idempotent.
- Use
--dry-run --jsonto preview generated requests, and ask the user to confirm before mutating more than 5 products. - Continue past per-product errors, collect each failure with its product/permalink, and summarize successes and failures at the end.
- For product media failures after creation, retry with the command printed in the error, such as
gumroad products covers add <id> --image ./cover.jpg.
Commands
auth — Manage authentication
# Check auth (do this first if unsure)
gumroad auth status --json --no-input
# Start device authorization and wait for human approval
gumroad auth login --no-input
# Use an existing seller token without browser approval
gumroad auth login --with-token --json --no-input < token.txt
printf '%s\n' "$GUMROAD_ACCESS_TOKEN" | gumroad auth login --with-token --json --no-input
# Print the active resolved seller token for another tool
gumroad auth token --no-input
# Force the local browser OAuth flow
gumroad auth login --web
# Logout
gumroad auth logout --yes --no-input
user — Account info
gumroad user --json --no-input
gumroad user --json --jq '.user.email' --no-input
# Update the seller name and/or bio. Pass an empty value to clear a field.
gumroad user update --name "Jane Doe" --bio "I make great things." --json --no-input
gumroad user update --bio "" --json --no-input
# Custom HTML profile landing page (authored by your agent; no checkout flags).
gumroad user page preview ./landing.html --json --no-input
gumroad user page publish ./landing.html --json --no-input
gumroad user page publish - --json --no-input < landing.html
gumroad user page clear --yes --json --no-input
gumroad user page url --no-input
gumroad user page url --json --jq '.profile_url' --no-input
refund-policy — Store-wide refund policy
# View the current account-level refund policy
gumroad refund-policy view --json --no-input
gumroad refund-policy view --json --jq '.refund_policy.in_effect' --no-input
# Set the refund period. Allowed values: none, 7, 14, 30, 183.
gumroad refund-policy set --period 30 --fine-print "Refund requests are reviewed within 2 business days." --json --no-input
# Clear fine print. This is account-level, not per-product.
gumroad refund-policy set --period none --fine-print "" --json --no-input
admin — Internal admin API
# Admin commands need internal admin auth.
# In agents/CI, set GUMROAD_ADMIN_TOKEN and pass --non-interactive.
# Inspect user identity, sign-in, social, risk, payout, and watchlist state
# Look up by --email, --user-id, or --username (resolves user_id > email > username)
gumroad admin users info --email seller@example.com --json --non-interactive --no-input
gumroad admin users info --username sellerone --json --non-interactive --no-input
# Review affiliate relationships
gumroad admin users affiliates --user-id 2245593582708 --direction granted --limit 50 --json --non-interactive --no-input
gumroad admin users affiliates --username sellerone --direction granted --limit 50 --json --non-interactive --no-input
gumroad admin users affiliates --email seller@example.com --direction received --cursor cur-next --json --non-interactive --no-input
# Read and add admin comments
gumroad admin users comments list --user-id 2245593582708 --type note --limit 50 --json --non-interactive --no-input
gumroad admin users comments list --username sellerone --type note --limit 50 --json --non-interactive --no-input
gumroad admin users comments add --user-id 2245593582708 --content "VAT exempt confirmed" --yes --json --non-interactive --no-input
# Account credits. credits add is a high-stakes write: dry-run first, then issue with explicit --yes.
# Amounts are cents, positive only, capped at $1,000 unless --allow-large-amount is explicitly passed.
gumroad admin users credits list --user-id 2245593582708 --limit 50 --json --non-interactive --no-input
gumroad admin users credits list --username sellerone --limit 50 --json --non-interactive --no-input
gumroad admin users credits add --user-id 2245593582708 --expected-email seller@example.com --amount-cents 1000 --reason "Goodwill for checkout bug" --dry-run --json --non-interactive --no-input
gumroad admin users credits add --user-id 2245593582708 --expected-email seller@example.com --amount-cents 1000 --reason "Goodwill for checkout bug" --yes --json --non-interactive --no-input
# Inspect compliance, Radar risk, and buyer history
gumroad admin users compliance --user-id 2245593582708 --json --non-interactive --no-input
gumroad admin users compliance --username sellerone --json --non-interactive --no-input
gumroad admin users radar --user-id 2245593582708 --limit 50 --json --non-interactive --no-input
gumroad admin users radar --username sellerone --limit 50 --json --non-interactive --no-input
gumroad admin users purchases --user-id 2245593582708 --status successful --has-early-fraud-warning=false --limit 50 --json --non-interactive --no-input
gumroad admin users purchases --username sellerone --status successful --limit 50 --json --non-interactive --no-input
gumroad admin users suspension --username sellerone --json --non-interactive --no-input
# Find related accounts by risk signals
gumroad admin users related --email seller@example.com --signal ip --signal payment_address --json --non-interactive --no-input
gumroad admin users related --username sellerone --signal ip --json --non-interactive --no-input
gumroad admin users related --email seller@example.com --json --jq '{related_users, truncated, per_signal_limit}' --non-interactive --no-input
# Mutate user compliance and suspension state.
# mark-compliant never lifts a suspension unless you ask for it. On a suspended
# account a plain mark-compliant is refused with a 422: the server assumes a
# caller that only reviewed the account's finances did not mean to reverse an
# enforcement decision it never looked at. To intentionally restore a suspended
# account, pass --clear-suspension, which is the only thing that sends
# clear_suspension: true.
gumroad admin users mark-compliant --user-id 2245593582708 --expected-email seller@example.com --note "Cleared after review" --yes --json --non-interactive --no-input
gumroad admin users mark-compliant --user-id 2245593582708 --expected-email seller@example.com --note "Suspension lifted after appeal review" --clear-suspension --yes --json --non-interactive --no-input
gumroad admin users suspend --user-id 2245593582708 --expected-email seller@example.com --note "Chargeback risk confirmed" --yes --json --non-interactive --no-input
gumroad admin users suspend-for-tos-violation --user-id 2245593582708 --expected-email seller@example.com --note "DMCA takedown notice confirmed" --yes --json --non-interactive --no-input
gumroad admin products flag-for-tos-violation <product-id> --user-id 2245593582708 --expected-email seller@example.com --yes --json --non-interactive --no-input
gumroad admin payouts scheduled create --user-id 2245593582708 --expected-email seller@example.com --processor stripe --payout-date 2026-06-15 --note "Appeal window closes before payout." --yes --json --non-interactive --no-input
gumroad admin payouts scheduled list --status pending --user-id 2245593582708 --json --non-interactive --no-input
# Recent payouts, with Stripe transfer id and destination bank account per row
gumroad admin payouts list --user-id 2245593582708 --limit 25 --json --jq '.recent_payouts[] | {external_id, stripe_transfer_id, bank_account}' --non-interactive --no-input
# Refund-balance dry-run still calls the preview GET, but skips the guarded POST.
gumroad admin users refund-balance --user-id 2245593582708 --expected-email seller@example.com --dry-run --json --non-interactive --no-input
gumroad admin users refund-balance --user-id 2245593582708 --expected-email seller@example.com --yes --json --non-interactive --no-input
# Queue fraud refunds for every remaining successful purchase of a suspended seller.
# Requires --expected-email and --expected-count (409 on mismatch or if a run is
# already queued). Buyers are NOT blocked by default; add --block-buyers only for
# buyer-fraud cases (self-purchase / card-testing rings). Refunds run in a
# background job; completion is recorded on the seller's comments and audit log.
gumroad admin users refund-all-for-fraud --user-id 2245593582708 --expected-email seller@example.com --expected-count 18 --yes --json --non-interactive --no-input
gumroad admin users refund-all-for-fraud --user-id 2245593582708 --expected-email seller@example.com --expected-count 18 --block-buyers --yes --json --non-interactive --no-input
# Inspect purchase and product fraud context
gumroad admin purchases view <purchase-id> --with-clusters --json --non-interactive --no-input
gumroad admin purchases search --email buyer@example.com --json --jq '{purchases, has_more, limit}' --non-interactive --no-input
gumroad admin purchases lookup --stripe-fingerprint fp_abc --limit 25 --json --non-interactive --no-input
# Refund a purchase. --reason is required: it is stored on the refund and shown to the
# creator in the "A sale has been refunded" notification email.
gumroad admin purchases refund <purchase-id> --email buyer@example.com --reason "Buyer reported being charged twice" --yes --json --non-interactive --no-input
gumroad admin purchases refund <purchase-id> --email buyer@example.com --amount 5.00 --reason "Partial refund agreed with buyer" --yes --json --non-interactive --no-input
gumroad admin products list --email seller@example.com --page 2 --per-page 25 --json --non-interactive --no-input
gumroad admin products view <product-id> --with-fraud-context --json --non-interactive --no-input
Shortened here. Read the whole file on GitHub.
Signals
- GitHub stars
- 53
- Forks
- 34
- Last commit
- Sep 2026
Advanced
- Catalog kind
- skill
- Gateway key
gumroad- Source
- github.com/antiwork/gumroad-cli