# Gumroad

> >

- **Type:** Skill
- **Install:** `agentstack add skill-antiwork-gumroad-cli-gumroad`
- **Verified:** Yes — security-reviewed for prompt injection and unsafe behavior
- **Seller:** [antiwork](https://agentstack.voostack.com/s/antiwork)
- **Installs:** 0
- **Category:** [Agent Skills](https://agentstack.voostack.com/c/agent-skills)
- **Latest version:** 0.1.0
- **License:** MIT
- **Upstream author:** [antiwork](https://github.com/antiwork)
- **Source:** https://github.com/antiwork/gumroad-cli/tree/main/skills/gumroad

## Install

```sh
agentstack add skill-antiwork-gumroad-cli-gumroad
```

Requires the [AgentStack CLI](https://agentstack.voostack.com/docs/cli). Works with Claude Code, Cursor, and any MCP-compatible agent.

## About

# gumroad CLI

Use `gumroad` (Gumroad CLI) to query and manage Gumroad data.

## Agent invariants

Always follow these rules:

- **Always** pass `--no-input` to prevent interactive prompts from blocking.
- **Always** pass `--json` for programmatic access.
- Use `--json --jq ` together to extract exactly what you need.
- For operations that can prompt for confirmation (delete, refund, mutating admin actions, `files abort`, `files complete` replay, product updates that remove files, or `products content set` when omitted page IDs will be deleted), add `--yes` to skip confirmation.
- Pass `--quiet` to suppress spinners and status messages.
- Pass `--dry-run` to preview mutating requests without executing them.
- Use `--page-delay 200ms` with `--all` to avoid rate limits on large datasets.
- Prices are in whole currency units (e.g. `--price 10.00` for $10), not cents. The CLI converts internally. Use `--currency eur` to change currency.
- Products are created as drafts — use `gumroad products publish ` 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  ./landing.html` to run the backend sanitizer without writing, `gumroad products page publish  ./landing.html` to store the page, `gumroad products page clear  --yes` to remove it, and `gumroad products page url ` to print the live URL. `--dry-run` only previews the CLI request body; it does not call the backend sanitizer. Inspect `.sanitization_report` in `preview` and `publish` JSON 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`, and `gumroad user page url` (prints the public profile URL and its `/landing/embed` URL). A profile has no buy button, so omit `data-gumroad-action="buy"` and the checkout data attributes; link to products instead.
- Product rich content uses `gumroad products content list  --json --no-input` to inspect page IDs, `gumroad products content get  --json --no-input` to dump the shared `rich_content` page array, and `gumroad products content set  content.json --dry-run --json --no-input` to preview a whole-document replacement. Without an explicit path, whole-document `set` reads `./content.json`; `set --page` reads `./page.json`. Use `--page ` with `get`/`set` to edit one matching page object; `set --page` still sends a merged whole-document PUT. For per-variant content, pass both `--variant ` and `--category `. Whole-document `set` deletes existing pages omitted from the JSON.
- Custom HTML pages can use `data-gumroad-field="name"`, `data-gumroad-field="price"`, `data-gumroad-field="description"`, and `data-gumroad-action="buy"`. To preselect checkout state, add `data-gumroad-option=""`, `data-gumroad-quantity=""`, `data-gumroad-price=""`, or `data-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  --json --no-input` and inspect `.preview_url` before `gumroad emails send  --yes --json --no-input`. Creating with `--send` publishes and blasts immediately, so use `--dry-run` first and require explicit human approval.
- If a command fails with a seller auth error, run `gumroad auth status --json --no-input` first. Agents can start seller auth with `gumroad auth login --no-input` and hand the printed approval URL to a human, or use an existing seller token via `GUMROAD_ACCESS_TOKEN` or `gumroad auth login --with-token`.
- For admin commands in agents/CI, pass `--non-interactive` and set `GUMROAD_ADMIN_TOKEN`; interactive shells can store an admin token with `gumroad auth login --web`.

## Response shapes

Most responses are wrapped in `{"success": true, ...}` with resource-specific keys:

- `user` → `.user`, `user update` → `.user`
- `user page preview` → `.custom_html`, `.sanitization_report`
- `user page publish` / `user page clear` → `.custom_html`, `.previous_custom_html`, `.profile_url`, `.sanitization_report`
- `user page url` → `.profile_url`, `.has_landing_page`
- `refund-policy view/set` → `.refund_policy`
- `products list` → `.products[]`
- `products view` → `.product`
- `products content get` → rich content page array directly, or one page object with `--page`
- `products content list` → rich content page summary array directly
- `products content set` → mutation envelope with `.result`
- `sales 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`
- `sales export` → `.status`, `.recipient_email`
- `sales summary` → `.gross_cents`, `.net_cents`, `.breakdown[]`
- `emails list` → `.emails[]`, `emails view/create/send` → `.email`, `emails send-preview` → `.preview_url`, `emails delete` → `.message`
- `payouts list` → `.payouts[]`, `payouts view/upcoming` → `.payout`
- `subscribers list` → `.subscribers[]`, `subscribers view` → `.subscriber`
- `licenses verify` → `.purchase`
- `offer-codes list` → `.offer_codes[]`
- `upsells list` → `.upsells[]`, `upsells view/create/update` → `.upsell`, `upsells delete` → `.message`
- `variant-categories list` → `.variant_categories[]`
- `variants list` → `.variants[]`
- `files upload` / `files complete` → `.file_url`
- `products create` with media flags → `.product` plus `.media[]`
- `products update` with media flags → `.product` plus `.media[]`
- `products covers add --image` → `.result.covers[]`, `.result.main_cover_id`, plus `.result.media[]`
- `products covers add --url` → `.result.covers[]`, `.result.main_cover_id`
- `products thumbnail set --image` → `.result.thumbnail`, plus `.result.media[]`
- `products thumbnail set --url` → `.result.thumbnail`
- `products page preview` → `.custom_html`, `.sanitization_report`
- `products page publish` / `products page clear` → `.product.custom_html`, `.product.landing_url`, `.previous_custom_html`, `.sanitization_report`
- `products update --custom-html` → `.product.custom_html`, `.product.landing_url`, `.previous_custom_html`, `.sanitization_report`
- Not every `products` write verb is flat: `create`, `update`, `unpublish`, and `delete` return top-level fields, but `covers add`, `thumbnail set`, and `content set` still wrap their payload in the `{success, …, result}` envelope — read those under `.result`
- `webhooks list` → `.resource_subscriptions[]`
- `admin users info` → `.user` (includes `.user.stripe` Stripe Connect state — `connected`, and when connected `stripe_connect_account_id`, `stripe_dashboard_url`, and a `verification` block of flags/counts or an `error` subfield when the live Stripe lookup failed — and `.user.admin_links` impersonate/user/purchases/stripe-dashboard URLs)
- `admin users affiliates` → `.affiliates[]`
- `admin users comments list` → `.comments[]`
- `admin users comments add` → `.comment`
- `admin 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_at`
- `admin users credits list` → `.credits[]`, `.pagination.next`
- `admin users radar` → `.radar_stats`, `.recent_efws[]`
- `admin users purchases` → `.purchases[]`
- `admin users related` → `.related_users[]`, `.truncated`, `.per_signal_limit`
- `admin users mark-compliant`, `admin users suspend`, `admin users suspend-for-tos-violation` → `.status`, `.message`, `.user_id`
- `admin products flag-for-tos-violation` → `.status`, `.message`, `.user_id`, `.product_id`
- `admin payouts list` → `.recent_payouts[]`, `.pagination.next`. Each payout carries `stripe_transfer_id` (a `po_…` payout or `py_…` destination payment, or null), `bank_account` (null for PayPal and debit-card payouts; otherwise `bank_number` routing/BIC, `account_holder_full_name`, `account_type`, `currency`), and `trace_id` (currently always null).
- `admin payouts scheduled create` → `.message`, `.user_id`, `.scheduled_payout`
- `admin users refund-balance` → `.status`, `.message`, `.user_id`, `.count`, `.total_amount_cents`, `.currency`
- `admin purchases view` → `.purchase`
- `admin purchases search` → `.purchases[]`, `.has_more`, `.limit`
- `admin 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`, and `admin purchases lookup` return `.pagination.next` as a cursor string. Pass it back with `--cursor`.
- Page-paginated: `admin products list` returns `.pagination.next` as an integer page number. Pass it back with `--page`; use `--per-page` for page size.
- Capped, not continuable: `admin users related` returns at most 50 related users per signal. Always inspect `.truncated`; when any signal is `true`, the result hit the cap and there is no cursor/page to fetch the rest.
- Capped, not continuable: `admin purchases search` returns `.has_more` when the server capped results. `--limit` is 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 --json` to 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  --image ./cover.jpg`.

## Commands

### auth — Manage authentication

```sh
# 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  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
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 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  --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

# Inspect purchase and product fraud context
gumroad admin purchases view  --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
gumroad admin products list --email seller@example.com --page 2 --per-page 25 --json --non-interactive --no-input
gumroad admin products view  --with-fraud-context --json --non-interactive --no-input

# Watchlist state does not pause payouts or change user risk state
gumroad admin users watch --user-id 2245593582708 --expected-email seller@example.co

…

## Source & license

This open-source skill is cataloged on AgentStack and links to its original source — we do not rehost the code.

- **Author:** [antiwork](https://github.com/antiwork)
- **Source:** [antiwork/gumroad-cli](https://github.com/antiwork/gumroad-cli)
- **License:** MIT

Install and usage instructions live in the source repository linked above.

## Pricing

- **Free** — Free

## Security capabilities

Automated source analysis of v0.1.0 — what this tool can access:

- **Network access:** no
- **Filesystem access:** no
- **Shell / process execution:** no
- **Environment & secrets:** no
- **Dynamic code execution:** no

*"Yes" means the capability is present in the source — more access means more to trust, not that it is unsafe.*


## Versions

- **0.1.0** — security scan: passed — Imported from the upstream source.

## Links

- Listing page: https://agentstack.voostack.com/l/skill-antiwork-gumroad-cli-gumroad
- Seller: https://agentstack.voostack.com/s/antiwork
- Browse the marketplace: https://agentstack.voostack.com/browse

---
Listed on AgentStack — the marketplace for AI agent skills and MCP servers. Every listing is security-reviewed. Creators keep 70%.
