The Shosho Seller Hub API
There are two ways to add a product on Shosho:
- 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.
- 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_quantityset to the spots available for that team.
Buyers claim a spot by purchasing that variant; inventory decrements automatically.
Related
- Products (NIP-99) — the Quick Add / Classified path.
- NIP-98 HTTP authentication
- Media uploads