For developers · MCP · REST · webhooks · SDK

The wishlist API
agents already know how to call.

Gish is the first wishlist on the Model Context Protocol. The MCP server is live at mcp.gishme.com; the REST API is live at api.gishme.com. OAuth scopes, 33 outbound webhook events delivered with HMAC-SHA256 signatures, llms.txt v2, PotentialAction schemas on every wish, and a one-line Save-to-Gish JS SDK for retailers. Built so an agent can save, search, contribute, and capture without scraping.

Read the docs
5 MCP tools live 33 webhook events OAuth 2.1 + PKCE OpenAPI 3.1 spec
Part I · agent-native commerce

Built for the agent layer.

Every wish on Gish is a structured action an agent can take, not a page it has to scrape.

Most commerce on the web was built for humans clicking buttons. The agent layer that’s now arriving — Claude in Chrome, ChatGPT actions, Cursor, OpenInterpreter, MCP-aware desktop apps — needs something different: a structured, callable surface where every meaningful object has a verb attached. Save this. List my wishes. Contribute to this one. Look up this URL. Capture this product. We built Gish that way from the start.

The Model Context Protocol is the substrate. mcp.gishme.com is a publicly-discoverable MCP server. Any Claude Desktop, Cursor, or compatible client can attach it in seconds, then call gish_save_wish, gish_list_wishes, gish_search, gish_contribute, and gish_capture_url with structured arguments. No browser automation. No DOM scraping. No screen pixels.

Part II · the MCP tools

Five tools at mcp.gishme.com.

These are the verbs an agent has on Gish today. They cover create, read, search, contribute, and capture.

gish_save_wish

Save a wish to the authenticated user’s list. Accepts URL, title, image, variant, occasion, deadline, budget. Returns the wish id and canonical URL. The most-called tool in production.

gish_list_wishes

List wishes for the authenticated user or for a public profile. Filter by occasion, status, owner. Pagination via cursor. Useful when an agent is preparing a gift-shortlist or a summary.

gish_search

Search the public explore index. Returns wishes the user has permission to see. The agent can query by SKU, brand, category, or natural-language intent (“running shoes for a marathon”).

gish_contribute

Place a contribution on a wish — full purchase, partial pledge, or group-buy split. Requires agent.write:pledges scope and a user-confirmation step at the wallet boundary.

gish_capture_url

Take any product URL and resolve it to a structured wish payload — brand, title, price, image, variant. Useful when the agent encountered a URL in conversation and wants to convert it.

gish_get_wish

Read a single wish by id or canonical URL. Returns the full structured object including occasion, budget, deadline, contributions to date, and PotentialAction links.

Part III · setup snippets

Wire it up in under a minute.

Claude Desktop, Cursor, ChatGPT custom GPT — the connection pattern is the same shape, with platform-specific config.

Claude Desktop

Add the Gish MCP server to claude_desktop_config.json. The config block:

// ~/Library/Application Support/Claude/claude_desktop_config.json { "mcpServers": { "gish": { "url": "https://mcp.gishme.com", "transport": "http", "auth": { "type": "oauth2", "client_id": "YOUR_CLIENT_ID" } } } }

Restart Claude. The Gish tools appear in the tool-picker. The first call triggers the OAuth flow; the user grants scopes; subsequent calls are silent.

Cursor

Cursor reads the same mcpServers shape in ~/.cursor/mcp.json. Same block, same flow.

ChatGPT custom GPT

For platforms that don’t speak MCP natively, point a custom GPT at the REST OpenAPI spec at api.gishme.com/openapi.json. ChatGPT’s GPT builder ingests it and exposes the same verbs as actions. OAuth is configured in the GPT’s authentication panel.

# curl-test the spec $ curl https://api.gishme.com/openapi.json | jq '.info' { "title": "Gish API", "version": "2026-06", "description": "The wishlist for the agent layer." }
Part IV · REST API

Same verbs, HTTP shape.

For runtimes that aren’t MCP-aware: a versioned REST API at api.gishme.com, OAuth 2.1 with PKCE, OpenAPI 3.1 spec, idempotency keys on all writes.

# save a wish $ curl -X POST https://api.gishme.com/v1/wishes \ -H "Authorization: Bearer $TOKEN" \ -H "Idempotency-Key: $UUID" \ -d '{ "url": "https://samsung.com/us/televisions/qn77s95f", "occasion": "birthday", "deadline": "2026-11-14", "budget_cents": 450000, "variant": { "size": "77in", "panel": "QD-OLED" } }' → 201 Created { "id": "wsh_8f2k…", "url": "https://gishme.com/w/8f2k" }

OAuth scopes

  • agent.read:wishes — read the user’s wishes, profile, contributions to date
  • agent.write:wishes — create, edit, archive wishes on the user’s behalf
  • agent.write:pledges — place contributions or group-buy pledges (requires wallet confirmation)
  • agent.read:contributions — read contribution history and receipts
  • agent.write:capture — call capture_url and import structured product data
  • agent.read:search — query the public explore index
  • partner.write:catalog — for retailer partners syncing product feeds (gated)

Scopes are user-grantable individually; the OAuth consent screen shows the exact verbs the agent is asking for, with plain-English summaries.

Unified scanner — /v1/scanner/resolve

One endpoint, three code shapes: UPC/EAN barcodes resolve to a product and land as a wish; TIC-prefixed UIDs verify product authenticity via The Ideal Code platform; Gish QR codes resolve to a wishlist or single wish. The client posts the raw decoded string; the server disambiguates by prefix and shape.

# resolve any scanned code $ curl -X POST https://api.gishme.com/v1/scanner/resolve \ -H "Authorization: Bearer $TOKEN" \ -d '{ "code": "0049000028911", "context": "in_store", "geo": { "lat": 37.77, "lng": -122.41 } }' → 200 OK { "kind": "upc", "product": { "sku": "…", "title": "…", "image": "…" }, "wish_draft_url": "https://gishme.com/capture/wsh_…" }

Free for all tiers. Scope: agent.write:capture. The Gish QR path is TIC-backed for unified scan analytics — the user-facing behavior (open the wishlist) is unchanged. See the TIC note below.

Part V · webhooks

33 events for closed-loop flows.

Subscribe at the application or per-user level. Every delivery is HMAC-SHA256 signed with a timestamp prefix (replay-protected), idempotent via a stable delivery id, and retried with exponential backoff at 1m, 5m, 30m, 1h, 6h, then 24h x3 (8 attempts, ~3 days). Deliveries that exhaust the schedule land in the dead-letter queue, replayable from the developer console.

Group buy

group_buy.created, pledge.received, goal.funded, goal.expired, group_buy.completed, thank_you_added

6 events

Contribution

contribution.intent_started, contribution.completed, contribution.refunded, contribution.failed, contribution.disputed

5 events

Treasury

treasury.created, treasury.deposit_made, treasury.deploy_proposed, treasury.deploy_executed, treasury.deploy_rejected

5 events

Booking

booking.queued, booking.assigned_to_ops, booking.confirmed, booking.refunded

4 events

User

user.created, user.deleted, user.connect_account_verified, subscription.tier_changed

4 events

Subscribe

// POST /v1/webhooks · Bearer-authed { "url": "https://example.com/hooks/gish", "events": ["wish.created", "contribution.completed"], "description": "Production analytics ingest" } // → 201 { "id": "whk_...", "signing_secret": "whsec_...", // shown ONCE "events": ["wish.created", "contribution.completed"], "is_active": true }

Headers on every delivery

POST /hooks/gish HTTP/1.1 Content-Type: application/json User-Agent: GishWebhooks/1.0 Gish-Webhook-Id: whk_abc123 Gish-Event-Id: evt_def456 Gish-Event-Type: wish.created Gish-Delivery-Id: whd_ghi789 Gish-Signature: t=1717800000,v1=8c5...e3a

Signature verification — Node

import { createHmac, timingSafeEqual } from "node:crypto"; function verifyGishSignature(header, rawBody, secret) { const parts = Object.fromEntries(header.split(",").map(kv => kv.split("="))); const expected = createHmac("sha256", secret) .update(`${parts.t}.${rawBody}`) .digest("hex"); const ok = timingSafeEqual(Buffer.from(expected), Buffer.from(parts.v1)); const fresh = Math.abs(Math.floor(Date.now / 1000) - Number(parts.t)) < 300; return ok && fresh; }

Signature verification — Python

import hmac, hashlib, time def verify_gish_signature(header: str, raw_body: bytes, secret: str) -> bool: parts = dict(p.split("=", 1) for p in header.split(",")) expected = hmac.new(secret.encode, f"{parts['t']}.{raw_body.decode}".encode, hashlib.sha256).hexdigest if not hmac.compare_digest(expected, parts["v1"]): return False return abs(int(time.time) - int(parts["t"])) < 300

Signature verification — Ruby

require "openssl" def verify_gish_signature(header, raw_body, secret) parts = header.split(",").map { |kv| kv.split("=", 2) }.to_h expected = OpenSSL::HMAC.hexdigest("sha256", secret, "#{parts['t']}.#{raw_body}") return false unless Rack::Utils.secure_compare(expected, parts["v1"]) (Time.now.to_i - parts["t"].to_i).abs < 300 end

Event reference

Every event payload follows the same envelope:

{ "id": "evt_...", "type": "wish.created", "created": 1717800000, "data": { /* event-specific, opaque IDs only — no PII */ } }

wish.created

A wish has been saved. Fires from POST /v1/wishes and the Save-to-Gish SDK retailer flow.

{ "wish_id": "wsh_...", "owner_id": "usr_...", "capture_surface": "extension", "kind": "item" }

wish.updated

Wish metadata changed (title, description, price, visibility, variants).

{ "wish_id": "wsh_...", "owner_id": "usr_...", "fields_changed": ["title", "price_amount"] }

wish.deleted

{ "wish_id": "wsh_...", "owner_id": "usr_..." }

wish.variants_confirmed

{ "wish_id": "wsh_...", "owner_id": "usr_...", "variant_count": 3 }

wish.price_dropped

Smart Savings (Sprint 22A) detected a price drop below the 30-day low. significant: true when delta > 15%.

{ "wish_id": "wsh_...", "owner_id": "usr_...", "new_price_cents": 24900, "retailer_domain": "amazon.com", "significant": true }

wishlist.created

{ "wishlist_id": "wl_...", "owner_id": "usr_...", "privacy": "link-only" }

wishlist.shared

Fires when a wishlist's privacy flips to public/link-only OR a new invite is sent.

{ "wishlist_id": "wl_...", "owner_id": "usr_...", "via": "invite", "role": "viewer" }

wishlist.member_added

{ "wishlist_id": "wl_...", "user_id": "usr_...", "role": "editor" }

wishlist.archived

{ "wishlist_id": "wl_...", "owner_id": "usr_..." }

group_buy.created

{ "group_id": "grp_...", "wish_id": "wsh_...", "organiser_id": "usr_...", "goal_cents": 500000, "currency": "USD", "deadline": 1720392000000 }

pledge.received

A contribution intent was started — money is authorized but not yet captured.

{ "contribution_id": "ctb_...", "group_id": "grp_...", "wish_id": "wsh_...", "amount_cents": 5000, "currency": "USD", "anonymous": false }

goal.funded

Aggregate captured-contribution total reached the group buy goal.

{ "group_id": "grp_...", "wish_id": "wsh_...", "raised_cents": 500000, "goal_cents": 500000, "currency": "USD" }

group_buy.completed

{ "group_id": "grp_...", "wish_id": "wsh_...", "raised_cents": 500000, "contributor_count": 12 }

goal.expired

Group buy deadline elapsed before goal was met. (Emitted by the group-expiry cron.)

{ "group_id": "grp_...", "wish_id": "wsh_...", "raised_cents": 312000, "goal_cents": 500000 }

thank_you_added

A wisher added a thank-you message to a captured contribution.

{ "contribution_id": "ctb_...", "wisher_id": "usr_...", "message_length": 128 }

contribution.intent_started

{ "contribution_id": "ctb_...", "group_id": "grp_...", "amount_cents": 5000, "currency": "USD" }

contribution.completed

Stripe captured the authorized payment intent. Money is now in the platform account awaiting payout.

{ "contribution_id": "ctb_...", "group_id": "grp_...", "amount_cents": 5000, "currency": "USD" }

contribution.refunded

{ "contribution_id": "ctb_...", "group_id": "grp_...", "amount_cents": 5000, "currency": "USD" }

contribution.failed

{ "contribution_id": "ctb_...", "group_id": "grp_...", "amount_cents": 5000, "reason": "card_declined" }

contribution.disputed

Stripe issued a chargeback notice. Surfaces in the fraud queue.

{ "contribution_id": "ctb_...", "group_id": "grp_...", "amount_cents": 5000 }

treasury.created

{ "treasury_id": "tre_...", "owner_id": "usr_...", "currency": "USD", "schedule": "monthly" }

treasury.deposit_made

{ "treasury_id": "tre_...", "member_id": "tm_...", "amount_cents": 25000, "currency": "USD" }

treasury.deploy_proposed

A founder initiated a deploy that requires a quorum vote (treasury has ≥ 2 active members).

{ "treasury_id": "tre_...", "deploy_id": "dep_...", "wish_id": "wsh_...", "amount_cents": 100000, "quorum_required": 3, "expires_at": 1718000000000 }

treasury.deploy_executed

{ "treasury_id": "tre_...", "deploy_id": "dep_...", "wish_id": "wsh_...", "amount_cents": 100000 }

treasury.deploy_rejected

{ "treasury_id": "tre_...", "deploy_id": "dep_...", "wish_id": "wsh_...", "yes_count": 1, "no_count": 3 }

booking.queued

{ "booking_identity_id": "bid_...", "wish_id": "wsh_...", "owner_id": "usr_..." }

booking.assigned_to_ops

{ "booking_id": "bkb_...", "wish_id": "wsh_...", "ops_user_id": "usr_..." }

booking.confirmed

Two-person rule satisfied (or amount < $1k); vendor PNR locked.

{ "booking_id": "bkb_...", "wish_id": "wsh_...", "amount_cents": 250000, "currency": "USD" }

booking.refunded

{ "booking_id": "bkb_...", "wish_id": "wsh_...", "amount_cents": 250000, "reason": "Vendor cancelled" }

user.created

{ "user_id": "usr_...", "signup_method": "apple" }

user.deleted

GDPR/CCPA hard-delete completed by the data-rights worker. Payload carries no PII.

{ "user_id": "usr_...", "reason": "user_requested", "region": "EU" }

user.connect_account_verified

Stripe Connect account is payouts-enabled. The user can now receive money.

{ "user_id": "usr_...", "stripe_account_id": "acct_...", "country": "US" }

subscription.tier_changed

{ "user_id": "usr_...", "prior_tier": 1, "new_tier": 3 }

Payloads carry opaque IDs only — no email addresses, full names, payment instruments, or other PII. Cross-reference on your side using the IDs and the authenticated read APIs. Signing secrets are rotated on demand from the developer console or via PATCH /v1/webhooks/:id. Per-webhook rate limit: 100 events/min/subscription.

Part VI · rate limits

Sane limits, elevation on request.

Most agents never touch the ceiling. If yours does, we’ll talk.

TierReq / minConcurrencyBurstIdentification
Anonymous30460 / minIP + UA fingerprint
User token60016900 / minOAuth user token
Agent service30032500 / minAgent client credentials
Partner / retailer2,000643,500 / minSigned JWT, partner scope
ElevatedcustomcustomcustomNegotiated SLA

Limit responses include X-RateLimit-Reset and Retry-After. Hit a ceiling repeatedly and the developer console surfaces a self-serve elevation request. Bulk-ingest retailer partners get a separate lane with idempotent batch endpoints up to 500 wishes per call.

Part VII · agent-native by construction

llms.txt v2 + PotentialAction everywhere.

Every wish surface is annotated so an agent that hasn’t even authenticated can still understand what actions are possible.

Gish ships an llms.txt v2 manifest at the root domain that lists the public surfaces, the canonical entrypoints, and the agent verbs available against each. Every wish page carries schema.org PotentialAction blocks: SaveAction, BuyAction, ReserveAction, DonateAction. The MCP tool surface and the JSON-LD on the page describe the same verbs, by design. An agent that reads either ends up at the same conclusion about what it can do.

// JSON-LD on every public wish page { "@context": "https://schema.org", "@type": "Product", "name": "Samsung 77in S95F OLED", "potentialAction": [ { "@type": "BuyAction", "target": "https://api.gishme.com/v1/wishes/8f2k/buy" }, { "@type": "DonateAction", "target": "https://api.gishme.com/v1/wishes/8f2k/contribute" }, { "@type": "SaveAction", "target": "https://api.gishme.com/v1/wishes/8f2k/clone" } ] }
Part VIII · Save-to-Gish SDK

One line for retailers.

Drop the script tag, get a Save-to-Gish button on every product page, with SKU-level capture.

<script src="https://sdk.gishme.com/save.js" data-sku="SAM-S95F-77" data-variant="size:77in,panel:QD-OLED" data-merchant="samsung"></script>

The SDK reads structured-data already on the page (microdata, JSON-LD, Open Graph) and falls back to the data-attributes you provide. A Save-to-Gish button renders adjacent to the retailer’s own “Add to wishlist” control. Click captures the SKU and variant exactly — no scraping, no guesswork. Attribution and affiliate tracking work out of the box.

Part IX · partner program

For platforms, retailers, and agent builders.

If you’re building something that needs structured wishlist intent, we’d like to talk.

Agent platform partners

You’re building a general-purpose agent and you want commerce verbs. We expose Gish as a first-class MCP server, give you elevated rate limits, and co-document the integration. No fee.

Open · apply via contact

Retailer partners

You operate a commerce surface and you want a one-tap Save-to-Gish button with SKU-level fidelity. We tune the SDK to your DOM, wire affiliate tracking, and co-publish on the retailers page.

Rolling · partner scope JWT

Editorial & AI tooling

You build editorial tools, gift-guide generators, or shopping assistants. We expose the public explore search through a dedicated read tier so your product can recommend real wishes, not synthetic ones.

Read-only · attribution required
Part X · why this matters

What changes when wishes are callable.

The commerce stack agents have today is a brittle assembly of headless browsers, brittle DOM selectors, and PDF receipts. Gish is the alternative shape.

An agent acting on a user’s behalf in 2026 needs three things from a commerce surface: a clean way to read what the user wants, a clean way to mutate it without screen-scraping, and a clean way to settle money with the user’s explicit consent at the moment it’s spent. Every commerce platform built before MCP gets one of those right at most, and usually achieves it by accident through a partner API that wasn’t designed for general agent access. We started from the opposite end.

Every wish on Gish is a first-class addressable object with a stable canonical URL, a structured payload, and a signed PotentialAction surface. Every state-changing call accepts an idempotency key so agents that retry on transient failure don’t accidentally double-charge or duplicate wishes. Every wallet action requires a user-confirmation step at the wallet boundary — the agent can propose a contribution, but only the user’s authenticated session can confirm spend above a per-scope ceiling. That boundary is non-negotiable, and we publish the ceiling explicitly so agent authors can design around it.

The result is the closest thing to a transactional commerce primitive that an agent can call without being a security incident waiting to happen. Saving a wish on a user’s behalf is safe. Listing contributions is safe. Even capturing a URL into a structured wish is safe, because the destination is an opinionated object inside the user’s account, not a free-form web request that could go anywhere. We’ve traded surface-area for safety in places that matter, and we’re explicit about it.

  • Idempotency keys are required on every write. Retries are first-class.
  • Wallet operations require user confirmation above a per-scope ceiling. Agents propose; users confirm.
  • Every object has a canonical URL that resolves to JSON-LD with PotentialAction blocks. Agents that haven’t authenticated can still understand the surface.
  • OAuth scopes are minimal and explicit — the consent screen names exactly what the agent will do, in plain English.
  • Webhooks are signed and idempotent with 24h exponential-backoff retries and a published signing-key rotation procedure.
  • Versioned API surface at /v1 with a 12-month deprecation policy when we ship breaking changes.
Part XI · honest constraints

What we don’t ship.

Where the line is, today.

Some of these will move; others won’t. We’d rather say what’s real than promise what isn’t. The MCP server today supports the six tools above; tools for browsing other users’ private wishlists, executing booking flows directly through the agent layer, and managing brand-week campaigns programmatically are roadmap items, not shipped. The REST API covers the full surface; the MCP surface is a curated subset chosen for safety and clarity.

The Save-to-Gish SDK is a one-line embed for product pages; for retailers wanting deeper integration (server-side product feed sync, real-time inventory, affiliate-network handoff) the partner-scope JWT is the right path, not the public SDK. The webhook fleet is comprehensive on wishes, contributions, treasury pools, and fund payouts; events for the editorial layer and the brand-week auction are not exposed yet. Rate limits are sane defaults; if you’re building something that’ll exceed them at launch, talk to us before you do, not after.

If a use case isn’t here, ask. The fastest way to get something added to the public surface is to be the first integration that needs it. We’ve shipped tools in days when the use case was clear and the spec was clean.

Part XII · example flows

Three end-to-end agent flows.

What a real agent integration looks like, top to bottom.

Flow one: gift-shortlist generation. A user asks their assistant for ten gift ideas for their sister’s thirtieth birthday on the 14th. The agent calls gish_list_wishes against the sister’s public profile, retrieves the wishes already there, then calls gish_search against the public explore index for adjacent items. It returns a shortlist annotated with deadlines, budget hints, and whether each item is already on her list. The user picks one; the agent calls gish_save_wish on its own user’s account with the gift-tag, opens a contribution flow, and hands off to the user’s wallet for confirmation. End-to-end, four MCP calls and one wallet confirmation.

Flow two: URL capture from a chat thread. A user pastes a product URL into a chat and says “remind me about this for the holidays.” The agent calls gish_capture_url with the URL and gets back a structured wish payload (brand, title, price, image, variant). It calls gish_save_wish with occasion: "holiday" and the user’s default holiday window. Total round-trip: two calls, zero scraping. The agent never touched the destination site’s DOM; Gish’s server-side resolver did the work.

Flow three: retailer-driven save. A retailer has the one-line Save-to-Gish SDK on their product detail page. A logged-in Gish user clicks the button. The SDK posts the SKU and variant directly to the API with a partner-scoped JWT and the user’s session token. The wish appears on the user’s list with affiliate attribution wired up, and a wish.created webhook fires to the retailer’s endpoint for their own analytics. End-to-end: one click, two server-side calls, zero scraping.

A common pattern across all three is that the agent never holds long-lived credentials beyond its OAuth refresh token, and every wallet-touching operation routes through a user-visible confirmation step. We considered shipping a higher-trust agent tier that would let an agent settle small contributions silently, and concluded the asymmetry of harm outweighed the convenience: the worst-case bug in an agent that can silently move money is much worse than the worst-case bug in an agent that has to ask. That tradeoff is conservative on purpose and we don’t expect it to relax soon.

All three flows are documented as full code samples in the developer console. They’re live; they’re what we test against; they’re the canonical examples we point new integrations at. If you have a fourth flow you want supported, the contact form on this page reaches the API team directly. Programmatic access to ad inventory is a separate auth path — see the advertisers page for that surface.

Note · Gish QR codes are powered by The Ideal Code (TIC) platform for unified product authentication and scan analytics. The UX is unchanged; the TIC backing gives Gish, retailer partners, and brand-week advertisers a single source of truth for scan events across UPC, TIC, and Gish QR shapes.

Build with declared intent.

The MCP server is live. The REST API is live. The SDK is live.