Skip to Content
DevelopersSeller Hub API (products & orders)

The Shosho Seller Hub API

There are two ways to add a product on Shosho:

  1. Quick Add — publish a NIP-99 listing (kind 30402). This is a Classified product: buyers press View on Website and buy on the seller’s own site. Any Nostr client can do it.
  2. Shosho Seller Hub — the API described here. The full toolset for a shop in any mode: options and variants, inventory, shipping, and orders. Required for Shopping Cart products (buyers check out on Shosho); also manages Classified products.

A product’s mode (Classified or Shopping Cart) is a property of the shop it lives in. A seller can run more than one shop — each with one mode and one currency — and products from all of a seller’s shops surface together on their shop page at https://shosho.live/shop/<npub>.

Base URL and auth

Discover the base URL and publishable key from GET https://shosho.live/api/config (storeApiUrl, storePublishableKey). This reference uses https://shop.shosho.live as the base; Seller Hub endpoints live under <base>/vendor.

Authentication is NIP-98 with the seller’s Nostr key — the same identity used everywhere else on Shosho; there is no separate password.

Every /vendor/* request needs two headers:

  • Authorization: Bearer <token>
  • x-seller-id: <seller id> — scopes the call to the seller’s shop.

What’s automatic

Don’t create these yourself — they’re provisioned for you:

  • When a shop is created it gets a default stock location, shipping profile, fulfillment provider, and sales-channel link.
  • When a product is created it is published, linked to the sales channel, given the shipping profile, and each stock-managed variant gets an inventory level at the default location, set to 0.

So the flow is: authenticate → set up the shop (if needed) → create products → update inventory quantities. Creating locations, profiles, channel links, or inventory levels duplicates the automatic setup and errors.

1. Authenticate

Set BASE=https://shop.shosho.live and PK=<storePublishableKey>.

Existing seller — log in:

AUTH=$(nak event -k 27235 -t u=$BASE/auth/member/nostr -t method=POST --sec <nsec1...> < /dev/null 2>/dev/null | base64) curl -s -X POST "$BASE/auth/member/nostr" -H "Content-Type: application/json" \ -d "{\"nip98_event\":\"$AUTH\",\"url\":\"$BASE/auth/member/nostr\"}" # → { "token": "<jwt>" }

Brand-new key — if login returns "No account linked to this Nostr pubkey", register to get a token, then set up the shop in step 2:

REG=$(nak event -k 27235 -t u=$BASE/auth/member/nostr/register -t method=POST --sec <nsec1...> < /dev/null 2>/dev/null | base64) curl -s -X POST "$BASE/auth/member/nostr/register" \ -H "Content-Type: application/json" -H "x-publishable-api-key: $PK" \ -d "{\"nip98_event\":\"$REG\",\"url\":\"$BASE/auth/member/nostr/register\"}" # → { "token": "<registration-jwt>" }

2. Find or set up the shop

TOKEN=<jwt> curl -s "$BASE/vendor/sellers" -H "Authorization: Bearer $TOKEN"

If none, create one — it needs name, email, member_email, and currency_code:

curl -s -X POST "$BASE/vendor/sellers" -H "Authorization: Bearer $TOKEN" \ -H "Content-Type: application/json" \ -d '{"name":"Ace Cards","email":"seller@example.com","member_email":"seller@example.com","currency_code":"usd"}' # → { "seller": { "id": "sel_..." } }

Set SID=<sel_...> and send it as x-seller-id on every /vendor/* call. Then log in again (the existing-seller call above) for a session TOKEN; the registration token only creates the shop.

A new shop is created in a pending state. The seller can save products and configure shipping right away, but the shop is only available to buyers once the Shosho team moves it to a published state.

3. Create a product

Product images take any public URL — see Media uploads. Send only product data; the shipping profile and sales channel are applied automatically.

curl -s -X POST "$BASE/vendor/products" -H "Authorization: Bearer $TOKEN" -H "x-seller-id: $SID" \ -H "Content-Type: application/json" -d '{ "title": "2026 Topps Chrome — Hobby Box", "status": "proposed", "images": [{ "url": "https://image.nostr.build/<hash>.jpg" }], "options": [{ "title": "Type", "values": ["Box"] }], "variants": [{ "title": "Box", "sku": "TOPPS26-BOX", "manage_inventory": true, "options": { "Type": "Box" }, "prices": [{ "currency_code": "usd", "amount": 250 }] }] }'

status: "proposed" is auto-published. prices.amount is in the currency’s major unit (250 = 250.00), in a currency the shop supports.

4. Set inventory

Each managed variant already has an inventory level at the default location, set to 0 — update the quantity (don’t create levels):

# stock location id: curl -s "$BASE/vendor/stock-locations?fields=id,name" -H "Authorization: Bearer $TOKEN" -H "x-seller-id: $SID" # variant → inventory item id: curl -s "$BASE/vendor/products/<prod_id>?fields=id,variants.sku,variants.inventory_items.inventory_item_id" \ -H "Authorization: Bearer $TOKEN" -H "x-seller-id: $SID" # update quantity: curl -s -X POST "$BASE/vendor/inventory-items/<inventory_item_id>/location-levels/<location_id>" \ -H "Authorization: Bearer $TOKEN" -H "x-seller-id: $SID" -H "Content-Type: application/json" \ -d '{"stocked_quantity":10}'

Recipe — a card break

A break is one product where each buyable spot is a variant:

  • one product for the break,
  • an option (e.g. Team) with a value per team/slot,
  • one variant per team, each with its own price and manage_inventory: true,
  • each variant’s stocked_quantity set to the spots available for that team.

Buyers claim a spot by purchasing that variant; inventory decrements automatically.

Last updated on