Skip to content

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 over S2S. Creating an order records the request (with a computed shippingCostMinor for 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’s balanceAfterMinor is null. (Cancelling an order the player placed from the game app, which did charge your wallet, credits that fee back: see Cancel an order.)

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"
}
FieldRequiredNotes
playerExternalId✅Must own every item.
inventoryItemIds✅1–50 inventory ids (from inventory). All must be unresolved and shippable, or the whole batch is rejected. Their values together must reach the operator’s shipping minimum (shipping.minShipmentValueMinor, 0 = none).
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",
"carrier": null,
"trackingNumber": null,
"trackingUrl": null,
"deliveredAt": null,
"replacementsSentAt": null,
"items": [
{ "inventoryItemId": 88, "itemId": 906, "name": "Golden Sword", "imageUrl": "https://…/sword.png", "valueMinor": 320, "currency": "EUR", "waitingForStock": false }
]
},
"balanceAfterMinor": null
}
FieldNotes
publicIdThe order handle — pass it to the detail / cancel / address endpoints below.
statusOrder status (pending, …, shipped_with_replacements).
shippingCostMinor, currencyThe computed cost, in the operator currency, for your reference — LootBox Solutions does not debit it. One fee per order: the shipping cost of the order’s most expensive item (an item’s own, or your default one when it has none).
realizedTotalCostMinorYour 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.
itemsValueTotalMinorThe summed value of the order’s items, in the operator currency — the always-present fallback for cost metrics before realizedTotalCostMinor is recorded.
userNotesThe player’s delivery note, or null.
carrier, trackingNumber, trackingUrlWhat your shipping desk entered for the parcel, each null until entered. trackingUrl is an optional http/https link to the carrier’s tracking page: offer the player a Track button only when it is set.
deliveredAtWhen the order was marked delivered, or null.
replacementsSentAtWhen the order was marked shipped_with_replacements, 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, and waitingForStock: true when the item was out of stock when shipping was requested, so the player can be told it is waiting for stock.
balanceAfterMinorAlways 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. A cancel refunds the shipping fee LootBox Solutions charged the player, and only that: an order created over S2S was never charged, so nothing is refunded (balanceAfterMinor stays null) — reverse your own shipping charge, if any, on your side. An order the player placed from the game app was charged from your wallet, and any cancel of it (yours over S2S, the player’s, your desk’s or the platform’s) credits that fee back to your wallet once (shipping:refund:<publicId> key; balanceAfterMinor stays null here). Whoever cancels, your webhook receives a fulfillment_notice saying what was refunded.

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. A later cancel refunds exactly what the player was charged, which is nothing for an order created over S2S. Past orders are unaffected by later edits: each order snapshots its own address at creation.

Errors

codeHTTPwhen
SHIPPING_DISABLED403shipping is turned off for your operator
INVENTORY_NOT_SHIPPABLE422an item is missing, not owned by the player, not unresolved, or not a shippable type — the whole batch is rejected
SHIPMENT_BELOW_MINIMUM422the items add up to less than the operator’s shipping minimum — nothing changes; error.minimumMinor, error.valueMinor and error.currency (the operator currency) say by how much, so you can tell the player to add items or wait
ORDER_NOT_FOUND404no order with that publicId for this player
SHIPMENT_NOT_CANCELLABLE422the order has moved past pending, so it can no longer be cancelled
SHIPMENT_NOT_EDITABLE422the 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.