TANITARDevelopers
GuidesShop

Shop

Sell items to your players for your own currencies, with server-side pricing, stock, per-player limits, fulfilment and reversal.

The shop lets your players spend the currencies you run in Tanitar on items you publish. It lives in Reward Management, so every purchase debits the same ledger your grants credit, and every order is tenant-scoped: an order, a listing or a code never crosses from one organizer to another.

You manage the shop from your back-end with an API key (this guide), or from the dashboard (see Shop in the dashboard). Your players buy through the player plane, usually in your own Player client using a short-lived Player token.

The model

RecordWhat it is
ListingOne sellable item, identified by a listingKey you choose. It has a title, a price in exactly one of your published currencies, an optional discount window, a fulfilment kind, a stock policy and an optional per-player limit. It moves draft → published → retired.
StockPer listing: unlimited, counted (a number of units left) or code-pool (a pool of codes you load).
CartOne open cart per player, holding award and code lines.
OrderThe durable record of a purchase. A purchase refused because of the catalog, the stock, the player's limit or their wallet is also an order, stored as rejected with its reason. A malformed request (for example an empty cart) is refused without creating one.

Every listing delivers through one of three fulfilment kinds:

KindWhat the player receivesStock policy
awardAn award (for example a badge) from one of your published award definitions, granted in the purchase transactionunlimited or counted
codeOne code per unit from the listing's code pool, delivered in the purchase transactioncode-pool only
manualSomething you deliver outside Tanitar; the order waits for your team to claim and fulfil itunlimited or counted

How a purchase settles

sequenceDiagram
    participant P as Your Player client
    participant RM as Reward Management
    participant DB as Tenant database
    participant YOU as Your integration
    participant WH as Your webhook receiver
    P->>RM: Checkout with Idempotency-Key
    RM->>RM: Recompute the price on the server, check currency, listing and award
    RM->>DB: One transaction: debit, stock claim, per-player limit, award grants, order, outbox
    alt refused
        RM-->>P: Typed refusal (insufficient funds, sold out, limit reached, price changed)
    else settled
        RM-->>P: The order
        DB-->>WH: shop.order.settled
        opt code listing
            P->>RM: Re-read the codes of the order at any time
        end
        opt manual listing
            YOU->>RM: Claim, then record fulfilment
            DB-->>WH: shop.order.fulfilled
        end
        opt reversal
            YOU->>RM: Reverse with a reason
            DB-->>WH: shop.order.reversed
        end
    end

Three guarantees hold for every purchase:

  • The server decides the price. The price a player pays is recomputed from the listing and its active discount at the moment of purchase. A player may send the total they expect; if it differs, the purchase is refused with SHOP_PRICE_CHANGED rather than silently repriced.
  • All or nothing. The debit, the stock claim, the per-player limit, any award grant and the order commit in one transaction. A refused purchase leaves no debit and no stock claimed; its rejected order records why.
  • Charged once. A retried purchase with the same Idempotency-Key returns the same order and never charges twice.

Scopes

The shop has its own scopes, separate from rewards:*, so each responsibility can be given to a different key:

ScopeGrants
shop:readRead listings and orders
shop:mutateCreate, edit, publish and retire listings; load codes into a pool
shop:fulfillClaim and fulfil manual orders
shop:reverseReverse an order and refund the player
shop:disclose-codeRead the code delivered on an order (audited)

None of these implies another. In particular, shop:mutate can load codes but never read one back.

Routes

All shop routes are on Reward Management ($REWARD_API_URL).

RouteScopeIdempotency-KeyGuide
GET /v1/shop/listingsshop:readNoneListings
GET /v1/shop/listings/:listingKeyshop:readNoneListings
POST /v1/shop/listingsshop:mutateRequiredListings
PATCH /v1/shop/listings/:listingKeyshop:mutateRequiredListings
POST /v1/shop/listings/:listingKey/publishshop:mutateRequiredListings
POST /v1/shop/listings/:listingKey/retireshop:mutateRequiredListings
POST /v1/shop/listings/:listingKey/stock/codesshop:mutateRequiredStock
GET /v1/shop/ordersshop:readNoneOrders
GET /v1/shop/orders/:orderIdshop:readNoneOrders
POST /v1/shop/orders/:orderId/claimshop:fulfillRequiredOrders
POST /v1/shop/orders/:orderId/fulfillmentshop:fulfillRequiredOrders
POST /v1/shop/orders/:orderId/code-disclosuresshop:disclose-codeNot usedOrders
POST /v1/shop/orders/:orderId/reversalshop:reverseRequiredReversal

Events

The shop publishes six events you can subscribe a webhook endpoint to:

EventWhen
shop.listing.publishedA listing is published, or a published listing is edited (a new revision)
shop.listing.retiredA listing is retired
shop.stock.depletedA published listing's counted stock or code pool reaches zero
shop.order.settledA purchase settles, including a refused purchase recorded as rejected
shop.order.fulfilledYour team records fulfilment of a manual order
shop.order.reversedAn order is reversed by your team or cancelled by the player

Payloads are in the event catalog.

On this page