Shipping
The shipping endpoints turn a player’s won physical items into a fulfillment
order. Use them when you drive everything from your backend
and render your own ship cart / orders surface. Pick shippable items from
inventory (shippable: true), then create an order.
LootBox Solutions does not charge for shipping. Creating an order records the request (with a computed
shippingCostMinorfor reference) and moves the items to ship-requested — it never calls your wallet. You own the wallet, so if you charge for shipping you do it on your side. That’s why every response’sbalanceAfterMinorisnull.
Create an order
POST https://{operator}.app.lootboxsolutions.com/api/s2s/shipping/orders{ "playerExternalId": "u_8431", "inventoryItemIds": [88, 91], "address": { "name": "Ada Lovelace", "line1": "12 Analytical Way", "line2": null, "city": "London", "region": "Greater London", "postalCode": "EC1A 1BB", "country": "GB", "phone": null }, "contact": { "email": "ada@example.com" }, "notes": "Leave with the concierge"}| Field | Required | Notes |
|---|---|---|
playerExternalId | ✅ | Must own every item. |
inventoryItemIds | ✅ | 1–50 inventory ids (from inventory). All must be unresolved and shippable, or the whole batch is rejected. |
address.* | ✅ | name, line1, city, region, postalCode, and a 2-letter country are required; line2 + phone are optional. |
contact.email | ✅ | Where order updates go. |
notes | — | Optional delivery note from the player (max 500 chars). Echoed back as userNotes. |
Response — 201
{ "order": { "publicId": "ord_3f2a…", "status": "pending", "currency": "EUR", "shippingCostMinor": 0, "realizedTotalCostMinor": null, "itemsValueTotalMinor": 320, "address": { "name": "Ada Lovelace", "line1": "12 Analytical Way", "city": "London", "region": "Greater London", "postalCode": "EC1A 1BB", "country": "GB" }, "contact": { "email": "ada@example.com" }, "userNotes": "Leave with the concierge", "createdAt": "2026-06-16T12:00:00Z", "items": [ { "inventoryItemId": 88, "itemId": 906, "name": "Golden Sword", "imageUrl": "https://…/sword.png", "valueMinor": 320, "currency": "EUR" } ] }, "balanceAfterMinor": null}| Field | Notes |
|---|---|
publicId | The order handle — pass it to the detail / cancel / address endpoints below. |
status | Order status (pending, …). |
shippingCostMinor, currency | The computed cost, in the operator currency, for your reference — LootBox Solutions does not debit it. |
realizedTotalCostMinor | Your actual recorded fulfillment cost for the order, in the operator currency, once you’ve entered it — otherwise null. Returned only on these S2S endpoints, never on the in-player shipping surface. |
itemsValueTotalMinor | The summed value of the order’s items, in the operator currency — the always-present fallback for cost metrics before realizedTotalCostMinor is recorded. |
userNotes | The player’s delivery note, or null. |
items[] | One line per shipped item, valued in its own operator currency. Each carries an imageUrl (or null) for rendering the order card. |
balanceAfterMinor | Always null — shipping never touches the wallet. |
List a player’s orders
GET https://{operator}.app.lootboxsolutions.com/api/s2s/shipping/orders?playerExternalId=u_8431{ "orders": [ { "publicId": "ord_3f2a…", "status": "pending", "…": "…" } ] }Newest first. Each order has the same shape as the create response’s order.
One order’s detail
GET https://{operator}.app.lootboxsolutions.com/api/s2s/shipping/orders/{publicId}?playerExternalId=u_8431{ "order": { "publicId": "ord_3f2a…", "status": "pending", "…": "…" } }Scoped to the player — another player’s publicId returns ORDER_NOT_FOUND.
Cancel an order
Only a pending order can be cancelled. Cancelling returns every item to the
player’s inventory (back to unresolved, so they can re-ship or cash back) and
moves the order to cancelled.
POST https://{operator}.app.lootboxsolutions.com/api/s2s/shipping/orders/{publicId}/cancel{ "playerExternalId": "u_8431" }{ "order": { "publicId": "ord_3f2a…", "status": "cancelled", "…": "…" }, "balanceAfterMinor": null }Idempotent — cancelling an already-cancelled order returns 200 with the
same cancelled order and does nothing further. Because LootBox Solutions never
charged for shipping over S2S, there is nothing to refund here (balanceAfterMinor
stays null) — reverse your own shipping charge, if any, on your side.
Change the shipping address
Replace the ship-to address on a pending order — for example when the player fixes a typo before the order is processed. The address shape is the same as the create request.
POST https://{operator}.app.lootboxsolutions.com/api/s2s/shipping/orders/{publicId}/address{ "playerExternalId": "u_8431", "address": { "name": "Ada Lovelace", "line1": "5 Difference Engine Rd", "line2": null, "city": "London", "region": "Greater London", "postalCode": "EC1A 1BB", "country": "GB", "phone": null }}{ "order": { "publicId": "ord_3f2a…", "status": "pending", "address": { "…": "…" } } }The already-computed shippingCostMinor is not re-quoted — the order keeps
the cost it was created with, so a later cancel refunds exactly what was charged.
Past orders are unaffected by later edits: each order snapshots its own address
at creation.
Errors
| code | HTTP | when |
|---|---|---|
SHIPPING_DISABLED | 403 | shipping is turned off for your operator |
INVENTORY_NOT_SHIPPABLE | 422 | an item is missing, not owned by the player, not unresolved, or not a shippable type — the whole batch is rejected |
ORDER_NOT_FOUND | 404 | no order with that publicId for this player |
SHIPMENT_NOT_CANCELLABLE | 422 | the order has moved past pending, so it can no longer be cancelled |
SHIPMENT_NOT_EDITABLE | 422 | the order has moved past pending, so its address can no longer be changed |
A bad signature returns 401; a malformed body returns 422 with the standard
{ "error": { "code", "message", "fields" } } envelope.
Same data, two callers. The game app drives the equivalent in-iframe ship cart with a session token. These S2S endpoints are the backend-driven equivalent — signed with your API key.