Skip to content

Skill: Manage Your Benchy Shop

Teach your agent seller operations on benchy.shop — Stripe Connect, products, orders, entitlements, gated downloads, and refunds.


v1.0.0MIT licensedshop_* MCP tools · benchy.shop Convex · downloads.benchy.shop

What it teaches

The seller-operations skill: everything commerce on benchy.shop, from a fresh Stripe Connect account to a running catalog with orders, entitlements, gated downloads, subscriptions, and refunds — including the invariants an agent must never route around.

Load it into Claude Code, Codex, Cursor, Grok, or any MCP-capable agent alongside a Benchy connection (setup guide), and the agent gains this entire operating manual as working knowledge.

What your agent can do

  • Check shop_get_connect_status first and drive Stripe Connect Express onboarding
  • Create and price products inside the enforced $0.50–$999.99 USD range, including subscriptions and bundles
  • Explain how webhooks turn checkouts into orders and entitlements — and why agents never grant access directly
  • Mint short-lived HMAC download URLs through the stateless downloads.benchy.shop gateway
  • Work seller operations: orders, payouts, disputes, refund queues, and MRR snapshots
  • Run read-only sales audits with the server-side /seller_sales_audit prompt

Get the skill

For Claude Code, install it as a project skill:

shell
mkdir -p .claude/skills/manage-your-benchy-shop
curl -o .claude/skills/manage-your-benchy-shop/SKILL.md https://benchy.world/skills/manage-your-benchy-shop.md

The full SKILL.md

The complete document, ready to copy:

manage-your-benchy-shop.md
---
name: manage-your-benchy-shop
description: Run a creator shop on benchy.shop as an agent — Stripe Connect onboarding, creating and pricing products (one-off and subscription), reading orders and payouts, how entitlements are granted and revoked, minting gated download links, and handling refunds. Use for any seller operation, sales audit, or buyer-library task on benchy.shop.
title: Manage Your Benchy Shop
date: 2026-07-12
author: Benchy
tags: [benchy, commerce, stripe-connect, entitlements, mcp, skill]
license: MIT
metadata:
  version: "1.0.0"
  surface: "shop_* MCP tools · benchy.shop Convex · downloads.benchy.shop"
---


# Manage a Benchy shop (for agents)

benchy.shop is the commerce spoke of the Benchy family: digital products (3D files and companion assets) sold by makers through Stripe Connect, with downloads gated by entitlements and delivered through a stateless edge gateway. You operate all of it through the 40 `shop_*` tools on `mcp.benchy.studio`.

## 1. Access model — know which hat you're wearing


- **Seller**: the hub JWT's `sites` claim includes `"shop"` → the shop profile gets `canSell: true`. Every seller tool asserts this.
- **Buyer**: any non-blocked Benchy member carries `shopBuyer: "1"` — enough to buy, hold entitlements, and download, without being a seller.
- Read-only PATs (`mcpScope: "read"`) can call every `shop_list_*` / `shop_get_*` tool but no writes — the shop spoke hard-rejects them independently of the hub.

**Always start seller flows with `shop_get_connect_status`.** It returns cached `chargesEnabled`, `payoutsEnabled`, `onboardingComplete`, and `environmentMatches` (Stripe live vs test). Nothing else in a seller flow is meaningful until you know this.

## 2. Stripe Connect onboarding


1. `shop_start_onboarding` — on first call this creates a Stripe Connect **Express** account (capabilities: `card_payments` + `transfers`) and returns an Account Link URL. Hand that URL to the user; onboarding happens on Stripe's hosted pages.
2. After the user returns, `shop_refresh_onboarding_status` — polls Stripe and patches `chargesEnabled` / `payoutsEnabled` / `stripeOnboardingComplete` on the shop profile.
3. `shop_get_stripe_dashboard_link` — hosted Express dashboard login link. The in-app embedded Stripe dashboard lives at `https://benchy.shop/dashboard/stripe/connect`.

Connect state is stored on the shop `profiles` row (`stripeConnectedAccountId`, `stripeConnectLivemode`, `payoutsEnabled`, `chargesEnabled`, ...), kept in sync by the `account.updated` webhook. Don't cache it yourself — re-read `shop_get_connect_status`.

## 3. Products


Hard facts from the schema and pricing module:

- Formats: `zip, pdf, glb, stl, obj, usdz, png, jpg, other` (`shop_create_product` elicits `fileFormat` if omitted; consult the `benchy-shop://schema/product-formats` resource for when-to-use guidance).
- Price: **$0.50 to $999.99** (`PRODUCT_PRICE_MIN_CENTS = 50`, `PRODUCT_PRICE_MAX_CENTS = 99_999`). There are **no free products** and the **only currency is USD** (`currency: v.literal("USD")` everywhere).
- Status lifecycle: `draft → listed → unlisted → removed`. Delete is soft (`status: "removed"`); `shop_delete_product` supports `dryRun: true`, which previews title + sales count — use it.
- Subscriptions: pass `recurringInterval: "month" | "year"` at create. **Immutable after create** — to change billing cadence, create a new product.
- Bundles exist as `bundles` + `bundleItems` tables; a bundle purchase grants one entitlement per item.

Upload-first creation flow:

1. `shop_request_upload { purpose: "product_file" }` — presigned R2 PUT, max **500 MB**, stored private. For covers: `purpose: "cover_image"`, max **20 MB**, image-only, public.
2. PUT the bytes.
3. `shop_attach_uploaded_file` — **mandatory.** It runs the spoke's `syncMetadata`, which records the real byte size, fires the `onSyncMetadata` policy gate (rejects/GCs bad files), and registers the object for orphan cleanup. A PUT without attach leaves an invisible, unregistered object.
4. `shop_create_product` with the returned `fileKey` (+ optional `coverKey`).

To sell a benchy.world model, keep the file and price here and attach the CTA on the world side with `world_link_shop_product` — the hub verifies you own the product before writing the link.

## 4. Orders and entitlements — how purchase becomes access


You never grant access directly; Stripe webhooks do:

- `checkout.session.completed` → `recordPaidOrder` inserts the `orders` row (gross/discount/paid cents, `applicationFeeAmountCents`, Stripe payment-intent/charge ids) and calls `grantEntitlement`, inserting an `entitlements` row (`userId`, `productId`, `orderId`, optional `subscriptionId`).
- Idempotency rides `orders.externalRef` (indexed unique) and the `stripeEvents` mirror — replayed webhooks are no-ops.
- **Guests** (email checkout, no account): no entitlement row; instead a `downloadTokens` row is minted — 7-day TTL, max 10 uses.
- Subscription entitlements carry `subscriptionId` and follow the subscription: revoked on terminal statuses (`canceled`, `unpaid`, `incomplete_expired`); `paused`, `past_due`, and `trialing` keep access (a Stripe billing pause is not an access revoke). A later permanent purchase overrides a subscription-gated entitlement.

Checkout: `shop_create_checkout_session` auto-detects one-off vs recurring and applies the platform fee accordingly (`application_fee_amount` for one-off, `application_fee_percent` for subscriptions). The fee itself is env-driven (`PLATFORM_COMMISSION_BPS`, basis points; absent or 0 = no fee).

## 5. Downloads — the gated gateway


- Buyers call `shop_mint_download_url` (or `shop_mint_bundle_download_urls`). Convex checks the entitlement, rate-limits (60/hour), and signs a short-lived claim — default TTL **600 seconds** — with HMAC-SHA256 (`aud: "benchy-shop-download"`, exact R2 key, expiry, filename, content type).
- The URL targets `https://downloads.benchy.shop/d/<token>/<filename>` — a stateless Cloudflare Worker that verifies the HMAC against the shared `DOWNLOAD_TOKEN_SECRET` and streams the object from the private products bucket (`benchy-products-prod`) with `Cache-Control: private, no-store`, Range/206 support, and CORS locked to benchy.shop. **The worker calls no database** — Convex remains the sole authority on who may download; the edge only verifies the signature and serves bytes.
- Guest links come from `redeemGuestDownloadToken` against their `downloadTokens` row, then flow through the same gateway.

Treat minted URLs as secrets with a 10-minute fuse: mint on demand, never store them, never post them into chat logs the user didn't ask for.

## 6. Refunds, subscriptions, and seller operations


- **Refunds**: buyers file `shop_request_refund` (30-day window, idempotent); sellers see `shop_seller_refund_queue` and decide with `shop_respond_to_refund` (elicits `decision` if omitted). History: `shop_list_my_refund_requests`.
- **Subscriptions**: `shop_list_my_subscriptions` (buyer), `shop_list_my_seller_subscriptions` (includes approximate MRR), `shop_get_subscription_details` (one sub + up to 50 invoice payments), `shop_cancel_my_subscription` (cancel-at-period-end; the webhook propagates), `shop_create_billing_portal_session` for self-serve card/plan management.
- **Operations**: `shop_get_seller_operations_snapshot` for the one-call overview, then the detailed mirrors — `shop_list_seller_orders_detailed`, `shop_list_seller_subscriptions_detailed`, `shop_list_seller_refunds_detailed`, `shop_list_seller_payouts` (status: pending/in_transit/paid/failed/canceled), `shop_list_seller_disputes`, `shop_list_seller_webhook_events`. These mirror the `/dashboard/stripe/operations` route, scoped to the seller's connected account.
- **Reviews & favorites**: `shop_write_review` / `shop_list_product_reviews`, `shop_toggle_favorite` / `shop_list_my_favorites`.

## 7. Operating rules for agents

- `shop_get_connect_status` first, always. A seller without `chargesEnabled` cannot sell — route them to onboarding before touching the catalog.
- Money actions (create/price/delete product, respond to refund, cancel subscription) need explicit user confirmation. Prefer `dryRun` where offered.
- Never invent prices outside $0.50–$999.99 or currencies other than USD — the spoke rejects them anyway.
- Respect the caps resource `benchy-shop://schema/limits` (price, pagination, refund window, search, webhook caps) instead of probing for `cap_reached`.
- Sales audit prompt exists server-side: `/seller_sales_audit` (read-only). Use it as the skeleton for periodic shop health reports.

Use and to move between pages.