Trading and gifting
Some games let one player hand an item or some currency to another — a gift, a trade, a drop for a teammate. This is that. If your game has no player-to-player economy, you do not need it; read inventory-and-catalogs.md for what a single player owns and stop there.
A transfer moves items and currency from one entity to another in a single call. It is separate from the rest of the inventory surface because it names two players, not one, and is reached as gem.transfers rather than through gem.me.
await gem.transfers.initiate({
to: { type: 'players', id: recipientId },
items: [{ itemDefinitionId: 'iron-sword', quantity: 1 }],
currencies: [{ currencyId: 'coins', amount: 50 }],
message: 'for the boss fight',
});Instant is the default, and it changes how you build the UI#
By default a transfer completes immediately. The value leaves the sender and arrives at the recipient in the one call, with no action required from the recipient — no accept, no confirm, no prompt. Build a gifting UI on the assumption that sending is the transaction, not that it opens a pending request.
accept() and reject() exist, but they are reachable only for a transfer that resolved to escrow — a held transfer the recipient must claim. An instant transfer is already done and cannot be accepted or rejected; calling either on one is an error, not a no-op. Do not write a "the gift is waiting" flow for the default mode, because there is no waiting.
// Only for a transfer that resolved to escrow:
await gem.transfers.accept(transferId);
await gem.transfers.reject(transferId);
// The sender can pull back an unclaimed escrow transfer:
await gem.transfers.cancel(transferId);The recipient is not told#
A transfer produces no event in the recipient's game. As everywhere else, there is no push into a sandboxed frame — the arriving gift is, by definition, a change some other player made, which is precisely the kind of change your game cannot be notified of. See inventory-and-catalogs.md for the full rule; the short version is that the recipient sees the gift the next time their game re-reads its inventory, so re-read after any action that could have involved another player, and design so a few seconds of not-yet-seeing-it is harmless.
transfer_policy is your game's rule, not the platform's#
An item definition can carry a transfer_policy — soulbound, account-bound, tradeable — and the platform honours it. But it is your setting, declared in your config, and it constrains an honest client. It is not a security boundary: it stops the game you shipped from moving an item it marked soulbound; it does not stop a modified client from attempting whatever the route allows. Treat it as a design rule you asked the platform to enforce for you, not as protection against a hostile client — that distinction is the same one inventory-and-catalogs.md makes about the whole economy.
The message is another player's text#
A transfer can carry a short message, and on the receiving side that message is text another player wrote. Up to a couple hundred characters of input you did not author arrive in your UI. Render it the way you render any untrusted string — through textContent or your framework's escaping — and never through innerHTML, and never build an img.src or a.href from it. The platform bounds its length and does light sanitising, but the rule that keeps you safe is your own rendering, not the platform's cleaning. This is the same population as an item's freeform properties and a player's display name: three places outside text reaches your code.
Errors#
| What you get | What happened | What to do |
|---|---|---|
| The transfer is refused with the recipient not found | No such player, or not one reachable from your game | Confirm the recipient id before sending; do not retry with the same id |
| A refusal naming the item's policy | The item is soulbound or account-bound and cannot move | This is your own transfer_policy doing its job; there is nothing to retry |
accept / reject refused | The transfer is instant (already complete) or already resolved | Only escrow transfers accept these; there is nothing pending to act on |
cancel refused | The escrow transfer was already claimed or already cancelled | Re-read its state; the window to pull it back has closed |
| The call times out | Unknown — it may have moved the value | Do not resend; re-read both sides' inventories and the transfer's state to see what happened |
What to read next#
inventory-and-catalogs.md— currencies, items, shops and loot, for one player.game-economy-tutorial.md— build a shop, step by step.