Keep a finished game playable, with its servers funded by its own players through your Steam store. You connect your publisher account, declare an Encore for the game, move its servers into a project of their own, and set the offers players can buy. Four methods are anonymous and are called by the game itself: a player is proven by a Steam auth ticket, and the Encore is named by the encore_key that ships in the game.
Connects a publisher account.
The credential is checked against the distributor before anything is stored, and the products it can act for are read at the same time. A key that does not work is refused rather than stored in a failed state, because a link nothing can use is one somebody builds an Encore on.
parentstringRequiredDISTRIBUTOR_STEAMnamestringIdentifierdisplay_namestringpublisher_keystringInput onlyRequiredA Steamworks publisher Web API key with Microtransaction permission.
Write-only. Sealed before it is stored and never returned by any call: it can refund every order the publisher has ever taken.
sandboxboolImmutableUse ISteamMicroTxnSandbox. Orders are not charged and credit nothing,
which is the whole integration minus the money.
distributor_link_idstringRequiredgrpcurl -H "authorization: Bearer $PLANY_API_KEY" \ -d '{ "parent": "accounts/", "distributor_link": { "display_name": "My server", "distributor": "DISTRIBUTOR_STEAM" }, "distributor_link_id": "my-distributor-link" }' \ api.plany.cloud:443 plany.encore.v1.EncoreService/CreateDistributorLink{ "name": "accounts//distributorLinks/my-distributor-link", "uid": "0b6f3c1e-8a2d-4f6e-9c1a-2f4b7d9e0a11", "display_name": "My server", "distributor": "DISTRIBUTOR_STEAM", "steam": { "sandbox": false, "publisher_key_fingerprint": "" }, "state": "STATE_ACTIVE", "state_message": "", "listings": [ { "distributor": "DISTRIBUTOR_STEAM", "product_id": "my-product", "display_name": "My server" } ], "outstanding": { "currency_code": "USD", "units": "5", "nanos": 0 }, "reserved": { "currency_code": "USD", "units": "5", "nanos": 0 }, "outstanding_limit": { "currency_code": "USD", "units": "5", "nanos": 0 }, "create_time": "2026-10-04T12:00:00Z", "update_time": "2026-10-04T12:00:00Z", "etag": "W/\"1\"", "distributor_share_basis_points": 0}namestringRequiredgrpcurl -H "authorization: Bearer $PLANY_API_KEY" \ -d '{ "name": "accounts//distributorLinks/my-distributor-link" }' \ api.plany.cloud:443 plany.encore.v1.EncoreService/GetDistributorLink{ "name": "accounts//distributorLinks/my-distributor-link", "uid": "0b6f3c1e-8a2d-4f6e-9c1a-2f4b7d9e0a11", "display_name": "My server", "distributor": "DISTRIBUTOR_STEAM", "steam": { "sandbox": false, "publisher_key_fingerprint": "" }, "state": "STATE_ACTIVE", "state_message": "", "listings": [ { "distributor": "DISTRIBUTOR_STEAM", "product_id": "my-product", "display_name": "My server" } ], "outstanding": { "currency_code": "USD", "units": "5", "nanos": 0 }, "reserved": { "currency_code": "USD", "units": "5", "nanos": 0 }, "outstanding_limit": { "currency_code": "USD", "units": "5", "nanos": 0 }, "create_time": "2026-10-04T12:00:00Z", "update_time": "2026-10-04T12:00:00Z", "etag": "W/\"1\"", "distributor_share_basis_points": 0}Lists your account's publisher links.
parentstringRequiredpage_sizeint32page_tokenstringgrpcurl -H "authorization: Bearer $PLANY_API_KEY" \ -d '{ "parent": "accounts/", "page_size": 50 }' \ api.plany.cloud:443 plany.encore.v1.EncoreService/ListDistributorLinks{ "distributor_links": [ { "name": "accounts//distributorLinks/my-distributor-link", "uid": "0b6f3c1e-8a2d-4f6e-9c1a-2f4b7d9e0a11", "display_name": "My server", "distributor": "DISTRIBUTOR_STEAM", "steam": { "sandbox": false, "publisher_key_fingerprint": "" }, "state": "STATE_ACTIVE", "state_message": "", "listings": [ { "distributor": "DISTRIBUTOR_STEAM", "product_id": "my-product", "display_name": "My server" } ], "outstanding": { "currency_code": "USD", "units": "5", "nanos": 0 }, "reserved": { "currency_code": "USD", "units": "5", "nanos": 0 }, "outstanding_limit": { "currency_code": "USD", "units": "5", "nanos": 0 }, "create_time": "2026-10-04T12:00:00Z", "update_time": "2026-10-04T12:00:00Z", "etag": "W/\"1\"", "distributor_share_basis_points": 0 } ], "next_page_token": ""}Updates the display name, or rotates the key.
A new key is checked, and must still own every product an Encore on this link names — rotating to a key for a different publisher would move a game's purchases onto somebody else's storefront.
DISTRIBUTOR_STEAMnamestringIdentifierdisplay_namestringpublisher_keystringInput onlyRequiredA Steamworks publisher Web API key with Microtransaction permission.
Write-only. Sealed before it is stored and never returned by any call: it can refund every order the publisher has ever taken.
sandboxboolImmutableUse ISteamMicroTxnSandbox. Orders are not charged and credit nothing,
which is the whole integration minus the money.
display_name and steam.publisher_key.
grpcurl -H "authorization: Bearer $PLANY_API_KEY" \ -d '{ "distributor_link": { "display_name": "My server", "distributor": "DISTRIBUTOR_STEAM" }, "update_mask": "display_name" }' \ api.plany.cloud:443 plany.encore.v1.EncoreService/UpdateDistributorLink{ "name": "accounts//distributorLinks/my-distributor-link", "uid": "0b6f3c1e-8a2d-4f6e-9c1a-2f4b7d9e0a11", "display_name": "My server", "distributor": "DISTRIBUTOR_STEAM", "steam": { "sandbox": false, "publisher_key_fingerprint": "" }, "state": "STATE_ACTIVE", "state_message": "", "listings": [ { "distributor": "DISTRIBUTOR_STEAM", "product_id": "my-product", "display_name": "My server" } ], "outstanding": { "currency_code": "USD", "units": "5", "nanos": 0 }, "reserved": { "currency_code": "USD", "units": "5", "nanos": 0 }, "outstanding_limit": { "currency_code": "USD", "units": "5", "nanos": 0 }, "create_time": "2026-10-04T12:00:00Z", "update_time": "2026-10-04T12:00:00Z", "etag": "W/\"1\"", "distributor_share_basis_points": 0}Removes a link. Refused while an Encore uses it, or once any purchase has been made through it: a link is also the record of what a studio owed.
namestringRequiredgrpcurl -H "authorization: Bearer $PLANY_API_KEY" \ -d '{ "name": "accounts//distributorLinks/my-distributor-link" }' \ api.plany.cloud:443 plany.encore.v1.EncoreService/DeleteDistributorLink{}Lists what a developer has paid Plany for credit already issued.
parentstringRequiredpage_sizeint32page_tokenstringgrpcurl -H "authorization: Bearer $PLANY_API_KEY" \ -d '{ "parent": "accounts//distributorLinks/my-distributor-link", "page_size": 50 }' \ api.plany.cloud:443 plany.encore.v1.EncoreService/ListSettlements{ "settlements": [ { "name": "accounts//distributorLinks/my-distributor-link/settlements/my-settlement", "amount": { "currency_code": "USD", "units": "5", "nanos": 0 }, "note": "", "recorded_by": "", "create_time": "2026-10-04T12:00:00Z" } ], "next_page_token": ""}Declares a game end of life, and starts verifying it.
Returns at once in STATE_VERIFYING; the evidence is gathered in the
background and the Encore moves to VERIFIED or NEEDS_REVIEW on its own.
Nothing about the game changes here — no project, no move. A developer may
transfer before verification finishes; players may not fund it until it has.
parentstringRequiredgamestringRequiredImmutableThe game, in the Game Registry: accounts/{account}/games/{game}.
distributor_linkstringRequiredImmutableThe publisher link that sells this game.
The product on the link's storefront. Must be one of its listings.
DISTRIBUTOR_STEAMproduct_idstringRequiredThe product in the distributor's own terms. For Steam, the app id in decimal — a string because an id is not a quantity, and because a 64-bit number does not survive a JavaScript client.
FUNDING_VISIBILITY_EXACT_BALANCEFUNDING_VISIBILITY_RUNWAYFUNDING_VISIBILITY_FUNDED_FLAGnamestringIdentifierdisplay_namestringetagstringencore_idstringRequiredgrpcurl -H "authorization: Bearer $PLANY_API_KEY" \ -d '{ "parent": "accounts/", "encore": { "display_name": "My server", "game": "", "distributor_link": "accounts//distributorLinks/my-distributor-link", "listing": { "distributor": "DISTRIBUTOR_STEAM", "product_id": "my-product" }, "funding_visibility": "FUNDING_VISIBILITY_EXACT_BALANCE" }, "encore_id": "my-encore" }' \ api.plany.cloud:443 plany.encore.v1.EncoreService/CreateEncore{ "name": "accounts//encores/my-encore", "uid": "0b6f3c1e-8a2d-4f6e-9c1a-2f4b7d9e0a11", "display_name": "My server", "game": "", "distributor_link": "accounts//distributorLinks/my-distributor-link", "listing": { "distributor": "DISTRIBUTOR_STEAM", "product_id": "my-product", "display_name": "My server" }, "encore_key": "", "state": "STATE_VERIFYING", "verification": { "evidence": [ { "kind": "KIND_STORE_UNAVAILABLE", "url": "", "quote": "", "detail": "", "observed_time": "2026-10-04T12:00:00Z" } ], "summary": "", "unknown_signals": [ "" ], "model": "", "decided_by": "", "reason": "", "decide_time": "2026-10-04T12:00:00Z" }, "project": "projects/", "transfer": { "mode": "MODE_CONVERT", "state": "STATE_RUNNING", "source_project": "", "items": [ { "type": "", "source": "", "destination": "", "state": "STATE_PENDING", "message": "", "client_facing": false } ], "message": "", "start_time": "2026-10-04T12:00:00Z", "end_time": "2026-10-04T12:00:00Z" }, "funding_visibility": "FUNDING_VISIBILITY_EXACT_BALANCE", "create_time": "2026-10-04T12:00:00Z", "update_time": "2026-10-04T12:00:00Z", "etag": "W/\"1\""}grpcurl -H "authorization: Bearer $PLANY_API_KEY" \ -d '{ "name": "accounts//encores/my-encore" }' \ api.plany.cloud:443 plany.encore.v1.EncoreService/GetEncore{ "name": "accounts//encores/my-encore", "uid": "0b6f3c1e-8a2d-4f6e-9c1a-2f4b7d9e0a11", "display_name": "My server", "game": "", "distributor_link": "accounts//distributorLinks/my-distributor-link", "listing": { "distributor": "DISTRIBUTOR_STEAM", "product_id": "my-product", "display_name": "My server" }, "encore_key": "", "state": "STATE_VERIFYING", "verification": { "evidence": [ { "kind": "KIND_STORE_UNAVAILABLE", "url": "", "quote": "", "detail": "", "observed_time": "2026-10-04T12:00:00Z" } ], "summary": "", "unknown_signals": [ "" ], "model": "", "decided_by": "", "reason": "", "decide_time": "2026-10-04T12:00:00Z" }, "project": "projects/", "transfer": { "mode": "MODE_CONVERT", "state": "STATE_RUNNING", "source_project": "", "items": [ { "type": "", "source": "", "destination": "", "state": "STATE_PENDING", "message": "", "client_facing": false } ], "message": "", "start_time": "2026-10-04T12:00:00Z", "end_time": "2026-10-04T12:00:00Z" }, "funding_visibility": "FUNDING_VISIBILITY_EXACT_BALANCE", "create_time": "2026-10-04T12:00:00Z", "update_time": "2026-10-04T12:00:00Z", "etag": "W/\"1\""}Lists your account's Encores. filter accepts a state, e.g. state = "NEEDS_REVIEW".
parentstringRequiredpage_sizeint32page_tokenstringfilterstringstate = "<STATE>", e.g. state = "NEEDS_REVIEW", and nothing else yet.
grpcurl -H "authorization: Bearer $PLANY_API_KEY" \ -d '{ "parent": "accounts/", "page_size": 50 }' \ api.plany.cloud:443 plany.encore.v1.EncoreService/ListEncores{ "encores": [ { "name": "accounts//encores/my-encore", "uid": "0b6f3c1e-8a2d-4f6e-9c1a-2f4b7d9e0a11", "display_name": "My server", "game": "", "distributor_link": "accounts//distributorLinks/my-distributor-link", "listing": { "distributor": "DISTRIBUTOR_STEAM", "product_id": "my-product", "display_name": "My server" }, "encore_key": "", "state": "STATE_VERIFYING", "verification": { "summary": "", "unknown_signals": [ "" ], "model": "", "decided_by": "", "reason": "", "decide_time": "2026-10-04T12:00:00Z" }, "project": "projects/", "transfer": { "mode": "MODE_CONVERT", "state": "STATE_RUNNING", "source_project": "", "message": "", "start_time": "2026-10-04T12:00:00Z", "end_time": "2026-10-04T12:00:00Z" }, "funding_visibility": "FUNDING_VISIBILITY_EXACT_BALANCE", "create_time": "2026-10-04T12:00:00Z", "update_time": "2026-10-04T12:00:00Z", "etag": "W/\"1\"" } ], "next_page_token": ""}Updates display_name and funding_visibility.
gamestringRequiredImmutableThe game, in the Game Registry: accounts/{account}/games/{game}.
distributor_linkstringRequiredImmutableThe publisher link that sells this game.
The product on the link's storefront. Must be one of its listings.
DISTRIBUTOR_STEAMproduct_idstringRequiredThe product in the distributor's own terms. For Steam, the app id in decimal — a string because an id is not a quantity, and because a 64-bit number does not survive a JavaScript client.
FUNDING_VISIBILITY_EXACT_BALANCEFUNDING_VISIBILITY_RUNWAYFUNDING_VISIBILITY_FUNDED_FLAGnamestringIdentifierdisplay_namestringetagstringdisplay_name and funding_visibility.
grpcurl -H "authorization: Bearer $PLANY_API_KEY" \ -d '{ "encore": { "display_name": "My server", "game": "", "distributor_link": "accounts//distributorLinks/my-distributor-link", "listing": { "distributor": "DISTRIBUTOR_STEAM", "product_id": "my-product" }, "funding_visibility": "FUNDING_VISIBILITY_EXACT_BALANCE" }, "update_mask": "display_name" }' \ api.plany.cloud:443 plany.encore.v1.EncoreService/UpdateEncore{ "name": "accounts//encores/my-encore", "uid": "0b6f3c1e-8a2d-4f6e-9c1a-2f4b7d9e0a11", "display_name": "My server", "game": "", "distributor_link": "accounts//distributorLinks/my-distributor-link", "listing": { "distributor": "DISTRIBUTOR_STEAM", "product_id": "my-product", "display_name": "My server" }, "encore_key": "", "state": "STATE_VERIFYING", "verification": { "evidence": [ { "kind": "KIND_STORE_UNAVAILABLE", "url": "", "quote": "", "detail": "", "observed_time": "2026-10-04T12:00:00Z" } ], "summary": "", "unknown_signals": [ "" ], "model": "", "decided_by": "", "reason": "", "decide_time": "2026-10-04T12:00:00Z" }, "project": "projects/", "transfer": { "mode": "MODE_CONVERT", "state": "STATE_RUNNING", "source_project": "", "items": [ { "type": "", "source": "", "destination": "", "state": "STATE_PENDING", "message": "", "client_facing": false } ], "message": "", "start_time": "2026-10-04T12:00:00Z", "end_time": "2026-10-04T12:00:00Z" }, "funding_visibility": "FUNDING_VISIBILITY_EXACT_BALANCE", "create_time": "2026-10-04T12:00:00Z", "update_time": "2026-10-04T12:00:00Z", "etag": "W/\"1\""}Deletes an Encore that has not been transferred.
After a transfer there is a project players paid into, and deleting the thing that explains it would leave a top-up-only project nobody can account for.
namestringRequiredgrpcurl -H "authorization: Bearer $PLANY_API_KEY" \ -d '{ "name": "accounts//encores/my-encore" }' \ api.plany.cloud:443 plany.encore.v1.EncoreService/DeleteEncore{}Gathers the evidence again, for an Encore waiting on review or declined.
namestringRequiredgrpcurl -H "authorization: Bearer $PLANY_API_KEY" \ -d '{ "name": "accounts//encores/my-encore" }' \ api.plany.cloud:443 plany.encore.v1.EncoreService/RetryVerification{ "name": "accounts//encores/my-encore", "uid": "0b6f3c1e-8a2d-4f6e-9c1a-2f4b7d9e0a11", "display_name": "My server", "game": "", "distributor_link": "accounts//distributorLinks/my-distributor-link", "listing": { "distributor": "DISTRIBUTOR_STEAM", "product_id": "my-product", "display_name": "My server" }, "encore_key": "", "state": "STATE_VERIFYING", "verification": { "evidence": [ { "kind": "KIND_STORE_UNAVAILABLE", "url": "", "quote": "", "detail": "", "observed_time": "2026-10-04T12:00:00Z" } ], "summary": "", "unknown_signals": [ "" ], "model": "", "decided_by": "", "reason": "", "decide_time": "2026-10-04T12:00:00Z" }, "project": "projects/", "transfer": { "mode": "MODE_CONVERT", "state": "STATE_RUNNING", "source_project": "", "items": [ { "type": "", "source": "", "destination": "", "state": "STATE_PENDING", "message": "", "client_facing": false } ], "message": "", "start_time": "2026-10-04T12:00:00Z", "end_time": "2026-10-04T12:00:00Z" }, "funding_visibility": "FUNDING_VISIBILITY_EXACT_BALANCE", "create_time": "2026-10-04T12:00:00Z", "update_time": "2026-10-04T12:00:00Z", "etag": "W/\"1\""}Proposes a transfer without doing anything.
Every component the game has in the source project, with the ones that belong to it pre-selected and the ones a shipped client may address by name flagged — and whether the project could be converted as it stands.
namestringRequiredsource_projectstringRequiredThe project the game's components are in now.
grpcurl -H "authorization: Bearer $PLANY_API_KEY" \ -d '{ "name": "accounts//encores/my-encore", "source_project": "projects/" }' \ api.plany.cloud:443 plany.encore.v1.EncoreService/PlanTransfer{ "candidates": [ { "type": "", "name": "", "display_name": "My server", "location": "", "belongs_to_game": false, "client_facing": false, "movable": false, "note": "" } ], "recommended_mode": "MODE_CONVERT", "explanation": "", "warnings": [ "" ]}Gives the Encore its project, by converting one or by moving into a new one.
Long-running. Follow its progress by reading the Encore with GetEncore: it carries the same Transfer as the operation's metadata, updated as each resource moves.
namestringRequiredsource_projectstringRequiredMODE_CONVERTMODE_MOVEresourcesrepeated stringThe candidates to move, by name. Refused for CONVERT, where the project goes whole and must hold nothing else.
acknowledge_warningsboolRequired for MOVE: the caller has read TransferPlan.warnings.
request_idstringgrpcurl -H "authorization: Bearer $PLANY_API_KEY" \ -d '{ "name": "accounts//encores/my-encore", "source_project": "projects/", "mode": "MODE_CONVERT" }' \ api.plany.cloud:443 plany.encore.v1.EncoreService/StartTransfer{ "name": "accounts//encores/my-encore", "uid": "0b6f3c1e-8a2d-4f6e-9c1a-2f4b7d9e0a11", "display_name": "My server", "game": "", "distributor_link": "accounts//distributorLinks/my-distributor-link", "listing": { "distributor": "DISTRIBUTOR_STEAM", "product_id": "my-product", "display_name": "My server" }, "encore_key": "", "state": "STATE_VERIFYING", "verification": { "evidence": [ { "kind": "KIND_STORE_UNAVAILABLE", "url": "", "quote": "", "detail": "", "observed_time": "2026-10-04T12:00:00Z" } ], "summary": "", "unknown_signals": [ "" ], "model": "", "decided_by": "", "reason": "", "decide_time": "2026-10-04T12:00:00Z" }, "project": "projects/", "transfer": { "mode": "MODE_CONVERT", "state": "STATE_RUNNING", "source_project": "", "items": [ { "type": "", "source": "", "destination": "", "state": "STATE_PENDING", "message": "", "client_facing": false } ], "message": "", "start_time": "2026-10-04T12:00:00Z", "end_time": "2026-10-04T12:00:00Z" }, "funding_visibility": "FUNDING_VISIBILITY_EXACT_BALANCE", "create_time": "2026-10-04T12:00:00Z", "update_time": "2026-10-04T12:00:00Z", "etag": "W/\"1\""}Accepts what a transfer cannot preserve for one item, and lets it continue.
namestringRequiredsourcestringRequiredTransferItem.source.
grpcurl -H "authorization: Bearer $PLANY_API_KEY" \ -d '{ "name": "accounts//encores/my-encore", "source": "" }' \ api.plany.cloud:443 plany.encore.v1.EncoreService/AcknowledgeTransferItem{ "name": "accounts//encores/my-encore", "uid": "0b6f3c1e-8a2d-4f6e-9c1a-2f4b7d9e0a11", "display_name": "My server", "game": "", "distributor_link": "accounts//distributorLinks/my-distributor-link", "listing": { "distributor": "DISTRIBUTOR_STEAM", "product_id": "my-product", "display_name": "My server" }, "encore_key": "", "state": "STATE_VERIFYING", "verification": { "evidence": [ { "kind": "KIND_STORE_UNAVAILABLE", "url": "", "quote": "", "detail": "", "observed_time": "2026-10-04T12:00:00Z" } ], "summary": "", "unknown_signals": [ "" ], "model": "", "decided_by": "", "reason": "", "decide_time": "2026-10-04T12:00:00Z" }, "project": "projects/", "transfer": { "mode": "MODE_CONVERT", "state": "STATE_RUNNING", "source_project": "", "items": [ { "type": "", "source": "", "destination": "", "state": "STATE_PENDING", "message": "", "client_facing": false } ], "message": "", "start_time": "2026-10-04T12:00:00Z", "end_time": "2026-10-04T12:00:00Z" }, "funding_visibility": "FUNDING_VISIBILITY_EXACT_BALANCE", "create_time": "2026-10-04T12:00:00Z", "update_time": "2026-10-04T12:00:00Z", "etag": "W/\"1\""}The project's money, as its developer sees it.
namestringRequiredgrpcurl -H "authorization: Bearer $PLANY_API_KEY" \ -d '{ "name": "accounts//encores/my-encore" }' \ api.plany.cloud:443 plany.encore.v1.EncoreService/GetFunding{ "encore": "", "project": "projects/", "balance": { "currency_code": "USD", "units": "5", "nanos": 0 }, "recent_spend": { "currency_code": "USD", "units": "5", "nanos": 0 }, "spend_window": "300s", "runway": "300s", "funded": false, "purchases": "0", "reversals": "0", "purchased": { "currency_code": "USD", "units": "5", "nanos": 0 }}parentstringRequireddisplay_namestringRequiredShown in the Steam overlay when a player confirms a purchase.
What the player pays, in USD. Steam converts to the player's own currency.
What the project is credited, in USD.
At most the price less the distributor's share. The developer is paid the net and owes Plany the credit, so a credit above the net is a developer owing more than they received on every sale.
namestringIdentifierdescriptionstringactiveboolWhether players can buy it now. Every offer is created active; deactivate one with UpdateOffer.
offer_idstringRequiredgrpcurl -H "authorization: Bearer $PLANY_API_KEY" \ -d '{ "parent": "accounts//encores/my-encore", "offer": { "display_name": "My server", "price": { "currency_code": "USD", "units": "5", "nanos": 0 }, "credit": { "currency_code": "USD", "units": "5", "nanos": 0 } }, "offer_id": "my-offer" }' \ api.plany.cloud:443 plany.encore.v1.EncoreService/CreateOffer{ "name": "accounts//encores/my-encore/offers/my-offer", "display_name": "My server", "description": "", "price": { "currency_code": "USD", "units": "5", "nanos": 0 }, "credit": { "currency_code": "USD", "units": "5", "nanos": 0 }, "active": false, "item_id": "0", "create_time": "2026-10-04T12:00:00Z", "update_time": "2026-10-04T12:00:00Z"}grpcurl -H "authorization: Bearer $PLANY_API_KEY" \ -d '{ "name": "accounts//encores/my-encore/offers/my-offer" }' \ api.plany.cloud:443 plany.encore.v1.EncoreService/GetOffer{ "name": "accounts//encores/my-encore/offers/my-offer", "display_name": "My server", "description": "", "price": { "currency_code": "USD", "units": "5", "nanos": 0 }, "credit": { "currency_code": "USD", "units": "5", "nanos": 0 }, "active": false, "item_id": "0", "create_time": "2026-10-04T12:00:00Z", "update_time": "2026-10-04T12:00:00Z"}parentstringRequiredpage_sizeint32page_tokenstringgrpcurl -H "authorization: Bearer $PLANY_API_KEY" \ -d '{ "parent": "accounts//encores/my-encore", "page_size": 50 }' \ api.plany.cloud:443 plany.encore.v1.EncoreService/ListOffers{ "offers": [ { "name": "accounts//encores/my-encore/offers/my-offer", "display_name": "My server", "description": "", "price": { "currency_code": "USD", "units": "5", "nanos": 0 }, "credit": { "currency_code": "USD", "units": "5", "nanos": 0 }, "active": false, "item_id": "0", "create_time": "2026-10-04T12:00:00Z", "update_time": "2026-10-04T12:00:00Z" } ], "next_page_token": ""}Updates an offer. A price change applies to purchases begun afterwards; one already open with the distributor settles at the price it was opened at.
display_namestringRequiredShown in the Steam overlay when a player confirms a purchase.
What the player pays, in USD. Steam converts to the player's own currency.
What the project is credited, in USD.
At most the price less the distributor's share. The developer is paid the net and owes Plany the credit, so a credit above the net is a developer owing more than they received on every sale.
namestringIdentifierdescriptionstringactiveboolWhether players can buy it now. Every offer is created active; deactivate one with UpdateOffer.
display_name, description, price, credit and active.
grpcurl -H "authorization: Bearer $PLANY_API_KEY" \ -d '{ "offer": { "display_name": "My server", "price": { "currency_code": "USD", "units": "5", "nanos": 0 }, "credit": { "currency_code": "USD", "units": "5", "nanos": 0 } }, "update_mask": "display_name" }' \ api.plany.cloud:443 plany.encore.v1.EncoreService/UpdateOffer{ "name": "accounts//encores/my-encore/offers/my-offer", "display_name": "My server", "description": "", "price": { "currency_code": "USD", "units": "5", "nanos": 0 }, "credit": { "currency_code": "USD", "units": "5", "nanos": 0 }, "active": false, "item_id": "0", "create_time": "2026-10-04T12:00:00Z", "update_time": "2026-10-04T12:00:00Z"}Deletes an offer nobody has bought. One with purchases is deactivated
instead, because a purchase has to keep naming what was bought — and its
offer_id stays taken. Read the offer afterwards to tell which happened.
namestringRequiredgrpcurl -H "authorization: Bearer $PLANY_API_KEY" \ -d '{ "name": "accounts//encores/my-encore/offers/my-offer" }' \ api.plany.cloud:443 plany.encore.v1.EncoreService/DeleteOffer{}namestringRequiredgrpcurl -H "authorization: Bearer $PLANY_API_KEY" \ -d '{ "name": "accounts//encores/my-encore/purchases/my-purchase" }' \ api.plany.cloud:443 plany.encore.v1.EncoreService/GetPurchase{ "name": "accounts//encores/my-encore/purchases/my-purchase", "offer": "accounts//encores/my-encore/offers/my-offer", "order_id": "my-order", "player_id": "my-player", "price": { "currency_code": "USD", "units": "5", "nanos": 0 }, "credit": { "currency_code": "USD", "units": "5", "nanos": 0 }, "state": "STATE_INITIATED", "state_message": "", "ledger_entry": "", "sandbox": false, "create_time": "2026-10-04T12:00:00Z", "credit_time": "2026-10-04T12:00:00Z", "reverse_time": "2026-10-04T12:00:00Z"}parentstringRequiredpage_sizeint32page_tokenstringgrpcurl -H "authorization: Bearer $PLANY_API_KEY" \ -d '{ "parent": "accounts//encores/my-encore", "page_size": 50 }' \ api.plany.cloud:443 plany.encore.v1.EncoreService/ListPurchases{ "purchases": [ { "name": "accounts//encores/my-encore/purchases/my-purchase", "offer": "accounts//encores/my-encore/offers/my-offer", "order_id": "my-order", "player_id": "my-player", "price": { "currency_code": "USD", "units": "5", "nanos": 0 }, "credit": { "currency_code": "USD", "units": "5", "nanos": 0 }, "state": "STATE_INITIATED", "state_message": "", "ledger_entry": "", "sandbox": false, "create_time": "2026-10-04T12:00:00Z", "credit_time": "2026-10-04T12:00:00Z", "reverse_time": "2026-10-04T12:00:00Z" } ], "next_page_token": ""}What a game may show its players about the servers' funding.
Anonymous. Shaped by Encore.funding_visibility, and names no account.
encore_keystringRequiredgrpcurl -H "authorization: Bearer $PLANY_API_KEY" \ -d '{ "encore_key": "" }' \ api.plany.cloud:443 plany.encore.v1.EncoreService/GetPublicFunding{ "funded": false, "balance": { "currency_code": "USD", "units": "5", "nanos": 0 }, "runway_days": "0", "visibility": "FUNDING_VISIBILITY_EXACT_BALANCE", "purchasable": false, "unavailable_reason": ""}The offers a game may put in its store.
Anonymous. Lists active offers even while the Encore is not yet verified,
with purchasable false, so a game can build its store before launch.
encore_keystringRequiredgrpcurl -H "authorization: Bearer $PLANY_API_KEY" \ -d '{ "encore_key": "" }' \ api.plany.cloud:443 plany.encore.v1.EncoreService/ListPublicOffers{ "offers": [ { "offer_id": "my-offer", "display_name": "My server", "description": "", "price": { "currency_code": "USD", "units": "5", "nanos": 0 } } ], "purchasable": false, "unavailable_reason": ""}Opens a purchase with the distributor. The Steam overlay appears for the player immediately after this returns.
Anonymous. The player is proven by auth_ticket and nothing else; the price
and the credit are the offer's, and are never taken from the request.
Refused before the distributor is called when the Encore is not verified,
the offer is inactive, or the publisher's outstanding limit would be
exceeded — so no player is charged for credit Plany will not issue.
encore_keystringRequiredoffer_idstringRequiredPublicOffer.offer_id.
auth_ticketstringRequiredThe player's proof of who they are. For Steam, the hex encoding of a ticket
from ISteamUser::GetAuthTicketForWebApi("plany-encore").
languagestringISO 639-1, for the overlay's text. Defaults to English.
request_idstringMakes a retry safe. A second call with the same id for the same Encore returns the purchase the first opened rather than opening a second order — without it a game cannot retry this call at all, because an ambiguous failure may already have put an order in front of the player.
grpcurl -H "authorization: Bearer $PLANY_API_KEY" \ -d '{ "encore_key": "", "offer_id": "my-offer", "auth_ticket": "" }' \ api.plany.cloud:443 plany.encore.v1.EncoreService/BeginPurchase{ "purchase": "", "order_id": "my-order"}Settles a purchase after the player answered in the overlay.
Anonymous, and safe to call more than once or not at all: the platform settles an approved order on its own if a game crashes before calling this, and a decline is confirmed with the distributor rather than taken from the request.
encore_keystringRequiredpurchasestringRequiredorder_idstringRequiredauthorizedboolRequiredWhat the overlay reported. A false is confirmed with the distributor
before the purchase is cancelled.
grpcurl -H "authorization: Bearer $PLANY_API_KEY" \ -d '{ "encore_key": "", "purchase": "", "order_id": "my-order", "authorized": false }' \ api.plany.cloud:443 plany.encore.v1.EncoreService/CompletePurchase{ "state": "STATE_INITIATED", "funding": { "funded": false, "balance": { "currency_code": "USD", "units": "5", "nanos": 0 }, "runway_days": "0", "visibility": "FUNDING_VISIBILITY_EXACT_BALANCE", "purchasable": false, "unavailable_reason": "" }}Evidence is one thing verification found.
kindEvidence.KindKIND_STORE_UNAVAILABLEKIND_OWNERSHIPKIND_ANNOUNCEMENTKIND_OPERATORurlstringWhere it was found.
quotestringThe words, verbatim, for an announcement. Checked against the post before it is recorded: a quote the model produced that is not in the post is discarded, never shown.
detailstringA sentence of context, e.g. the store's own availability text.
observed_timeTimestamp{ "kind": "KIND_STORE_UNAVAILABLE", "url": "", "quote": "", "detail": "", "observed_time": "2026-10-04T12:00:00Z"}Listing is one product on one storefront.
DISTRIBUTOR_STEAMproduct_idstringRequiredThe product in the distributor's own terms. For Steam, the app id in decimal — a string because an id is not a quantity, and because a 64-bit number does not survive a JavaScript client.
display_namestringOutput onlyThe storefront's own name for it, when Plany has read one.
{ "distributor": "DISTRIBUTOR_STEAM", "product_id": "my-product", "display_name": "My server"}PublicOffer is an offer as a game shows it.
offer_idstringThe offer's own id, to pass to BeginPurchase.
display_namestringdescriptionstringpriceMoney{ "offer_id": "my-offer", "display_name": "My server", "description": "", "price": { "currency_code": "USD", "units": "5", "nanos": 0 }}Settlement is a developer paying Plany for credit already issued.
{ "name": "accounts//distributorLinks/my-distributor-link/settlements/my-settlement", "amount": { "currency_code": "USD", "units": "5", "nanos": 0 }, "note": "", "recorded_by": "", "create_time": "2026-10-04T12:00:00Z"}SteamCredential is how Plany acts as a Steamworks publisher.
publisher_keystringInput onlyRequiredA Steamworks publisher Web API key with Microtransaction permission.
Write-only. Sealed before it is stored and never returned by any call: it can refund every order the publisher has ever taken.
sandboxboolImmutableUse ISteamMicroTxnSandbox. Orders are not charged and credit nothing,
which is the whole integration minus the money.
publisher_key_fingerprintstringOutput onlyA fingerprint of the key, so a rotation can be seen to have happened: the first 16 hexadecimal characters of the SHA-256 of the key as sent. A client holding a key can compute it and compare without the key ever being read back.
{ "sandbox": false, "publisher_key_fingerprint": ""}Transfer is how an Encore came to have a project.
modeTransfer.ModeMODE_CONVERTMODE_MOVEstateTransfer.StateSTATE_RUNNINGSTATE_SUCCEEDEDSTATE_FAILEDsource_projectstringtypestringThe resource type, e.g. playpute.plany.cloud/GameServer.
sourcestringdestinationstringstateTransferItem.StateSTATE_PENDINGSTATE_RUNNINGSTATE_SUCCEEDEDSTATE_FAILEDSTATE_NEEDS_ACKNOWLEDGEMENTmessagestringclient_facingboolSomething a shipped game client may address by name. Moving it changes that name, so a client nobody can patch stops reaching it.
messagestringstart_timeTimestampend_timeTimestamp{ "mode": "MODE_CONVERT", "state": "STATE_RUNNING", "source_project": "", "items": [ { "type": "", "source": "", "destination": "", "state": "STATE_PENDING", "message": "", "client_facing": false } ], "message": "", "start_time": "2026-10-04T12:00:00Z", "end_time": "2026-10-04T12:00:00Z"}TransferCandidate is one resource a transfer could take.
typestringe.g. playpute.plany.cloud/GameServer.
namestringdisplay_namestringlocationstringbelongs_to_gameboolFound through the game itself, rather than only by being in the project.
client_facingboolA shipped game client may address it by name.
movableboolWhether a move of this type is supported. A candidate that is not is listed so the plan can say why CONVERT is not available, never silently left behind.
notestring{ "type": "", "name": "", "display_name": "My server", "location": "", "belongs_to_game": false, "client_facing": false, "movable": false, "note": ""}TransferItem is one resource moving into an Encore project.
typestringThe resource type, e.g. playpute.plany.cloud/GameServer.
sourcestringdestinationstringstateTransferItem.StateSTATE_PENDINGSTATE_RUNNINGSTATE_SUCCEEDEDSTATE_FAILEDSTATE_NEEDS_ACKNOWLEDGEMENTmessagestringclient_facingboolSomething a shipped game client may address by name. Moving it changes that name, so a client nobody can patch stops reaching it.
{ "type": "", "source": "", "destination": "", "state": "STATE_PENDING", "message": "", "client_facing": false}Verification is what Plany found, and who decided.
evidencerepeated EvidencekindEvidence.KindKIND_STORE_UNAVAILABLEKIND_OWNERSHIPKIND_ANNOUNCEMENTKIND_OPERATORurlstringWhere it was found.
quotestringThe words, verbatim, for an announcement. Checked against the post before it is recorded: a quote the model produced that is not in the post is discarded, never shown.
detailstringA sentence of context, e.g. the store's own availability text.
observed_timeTimestampsummarystringWhy the platform did or did not decide on its own, in a sentence.
unknown_signalsrepeated stringSignals that could not be read, by name. A signal that could not be read is not a signal that fell short, and a reviewer needs to know which.
modelstringThe model that read the announcements, when one did.
decided_bystringencore.plany.cloud for an automatic decision, or the person at Plany who reviewed it.
reasonstringThe reviewer's own sentence, for a decision a person made.
decide_timeTimestamp{ "evidence": [ { "kind": "KIND_STORE_UNAVAILABLE", "url": "", "quote": "", "detail": "", "observed_time": "2026-10-04T12:00:00Z" } ], "summary": "", "unknown_signals": [ "" ], "model": "", "decided_by": "", "reason": "", "decide_time": "2026-10-04T12:00:00Z"}purchasestringAn opaque handle to pass to CompletePurchase.
order_idstringThe distributor's order id — for Steam, the m_ulOrderID the
MicroTxnAuthorizationResponse_t callback will carry.
statePurchase.StateSTATE_INITIATEDSTATE_AUTHORIZEDSTATE_CREDITEDSTATE_CANCELLEDSTATE_FAILEDSTATE_REVERSEDfundingPublicFundingfundedboolbalanceMoneySet only for FUNDING_VISIBILITY_EXACT_BALANCE.
runway_daysint64Whole days, for EXACT_BALANCE and RUNWAY.
Optional, and absent means "no runway to show" — nothing was spent recently to project from, or the visibility hides it. Zero means less than a day left. A plain integer could not tell those apart, and a game showing "0 days left" on a quiet, well-funded server would be telling its players something false and alarming.
visibilityFundingVisibilityFUNDING_VISIBILITY_EXACT_BALANCEFUNDING_VISIBILITY_RUNWAYFUNDING_VISIBILITY_FUNDED_FLAGpurchasableboolWhether a player can buy an offer right now, and why not if they cannot.
unavailable_reasonstringDistributorLink is your publisher account, connected to Plany.
It is also a line of credit. A player pays Steam, Steam pays you on its own schedule, and you owe Plany for the credit Plany has already put in the project. Plany extends that credit up to outstanding_limit, which Plany sets and which starts at zero — so a link with no limit cannot sell anything yet, and a purchase that would exceed it is refused before any player is charged.
DISTRIBUTOR_STEAMnamestringIdentifieruidstringOutput onlydisplay_namestringpublisher_keystringInput onlyRequiredA Steamworks publisher Web API key with Microtransaction permission.
Write-only. Sealed before it is stored and never returned by any call: it can refund every order the publisher has ever taken.
sandboxboolImmutableUse ISteamMicroTxnSandbox. Orders are not charged and credit nothing,
which is the whole integration minus the money.
publisher_key_fingerprintstringOutput onlyA fingerprint of the key, so a rotation can be seen to have happened: the first 16 hexadecimal characters of the SHA-256 of the key as sent. A client holding a key can compute it and compare without the key ever being read back.
STATE_ACTIVESTATE_CREDENTIAL_INVALIDstate_messagestringOutput onlyEvery product this credential can act for.
Discovered from the distributor rather than declared — for Steam,
ISteamApps/GetPartnerAppListForWebAPIKey — and that discovery is the
proof a developer owns a game. An Encore can only name a listing here.
DISTRIBUTOR_STEAMproduct_idstringRequiredThe product in the distributor's own terms. For Steam, the app id in decimal — a string because an id is not a quantity, and because a 64-bit number does not survive a JavaScript client.
display_namestringOutput onlyThe storefront's own name for it, when Plany has read one.
Credit Plany has issued against this link and not been settled for.
Credit held for purchases a player has started and not finished. Counted against the limit, so two players cannot both take the last of it.
The most unsettled credit this link may carry. Set by Plany.
etagstringOutput onlydistributor_share_basis_pointsint32Output onlyThe distributor's share of a sale, out of 10,000. An offer's credit may be at most its price less this, which is how a form can show the largest credit allowed before anybody submits one.
Encore is an end-of-life game kept playable by its players.
gamestringRequiredImmutableThe game, in the Game Registry: accounts/{account}/games/{game}.
distributor_linkstringRequiredImmutableThe publisher link that sells this game.
The product on the link's storefront. Must be one of its listings.
DISTRIBUTOR_STEAMproduct_idstringRequiredThe product in the distributor's own terms. For Steam, the app id in decimal — a string because an id is not a quantity, and because a 64-bit number does not survive a JavaScript client.
display_namestringOutput onlyThe storefront's own name for it, when Plany has read one.
FUNDING_VISIBILITY_EXACT_BALANCEFUNDING_VISIBILITY_RUNWAYFUNDING_VISIBILITY_FUNDED_FLAGnamestringIdentifieruidstringOutput onlydisplay_namestringencore_keystringOutput onlyThe handle a game ships to name this Encore without a credential.
Opaque and names no account: it is in every copy of the game, and what it unlocks is reading public funding and starting a purchase — both of which anybody who owns the game may do.
STATE_VERIFYINGSTATE_NEEDS_REVIEWSTATE_VERIFIEDSTATE_DECLINEDSTATE_REVOKEDevidencerepeated EvidencekindEvidence.KindKIND_STORE_UNAVAILABLEKIND_OWNERSHIPKIND_ANNOUNCEMENTKIND_OPERATORurlstringWhere it was found.
quotestringThe words, verbatim, for an announcement. Checked against the post before it is recorded: a quote the model produced that is not in the post is discarded, never shown.
detailstringA sentence of context, e.g. the store's own availability text.
observed_timeTimestampsummarystringWhy the platform did or did not decide on its own, in a sentence.
unknown_signalsrepeated stringSignals that could not be read, by name. A signal that could not be read is not a signal that fell short, and a reviewer needs to know which.
modelstringThe model that read the announcements, when one did.
decided_bystringencore.plany.cloud for an automatic decision, or the person at Plany who reviewed it.
reasonstringThe reviewer's own sentence, for a decision a person made.
decide_timeTimestampprojectstringOutput onlyThe top-up-only project, once there is one.
modeTransfer.ModeMODE_CONVERTMODE_MOVEstateTransfer.StateSTATE_RUNNINGSTATE_SUCCEEDEDSTATE_FAILEDsource_projectstringtypestringThe resource type, e.g. playpute.plany.cloud/GameServer.
sourcestringdestinationstringstateTransferItem.StateSTATE_PENDINGSTATE_RUNNINGSTATE_SUCCEEDEDSTATE_FAILEDSTATE_NEEDS_ACKNOWLEDGEMENTmessagestringclient_facingboolSomething a shipped game client may address by name. Moving it changes that name, so a client nobody can patch stops reaching it.
messagestringstart_timeTimestampend_timeTimestampetagstringFunding is a project's money, as its developer sees it.
encorestringprojectstringbalanceMoneyNegative after a refund of credit already spent.
recent_spendMoneyspend_windowDurationrunwayDurationHow long the balance lasts at the recent rate. Unset when nothing was spent in the window, because "forever" is not a runway.
fundedboolpurchasesint64Player purchases credited and reversed over the window.
reversalsint64purchasedMoneydistributor_linksrepeated DistributorLinkDISTRIBUTOR_STEAMnamestringIdentifieruidstringOutput onlydisplay_namestringpublisher_keystringInput onlyRequiredA Steamworks publisher Web API key with Microtransaction permission.
Write-only. Sealed before it is stored and never returned by any call: it can refund every order the publisher has ever taken.
sandboxboolImmutableUse ISteamMicroTxnSandbox. Orders are not charged and credit nothing,
which is the whole integration minus the money.
publisher_key_fingerprintstringOutput onlyA fingerprint of the key, so a rotation can be seen to have happened: the first 16 hexadecimal characters of the SHA-256 of the key as sent. A client holding a key can compute it and compare without the key ever being read back.
STATE_ACTIVESTATE_CREDENTIAL_INVALIDstate_messagestringOutput onlyEvery product this credential can act for.
Discovered from the distributor rather than declared — for Steam,
ISteamApps/GetPartnerAppListForWebAPIKey — and that discovery is the
proof a developer owns a game. An Encore can only name a listing here.
DISTRIBUTOR_STEAMproduct_idstringRequiredThe product in the distributor's own terms. For Steam, the app id in decimal — a string because an id is not a quantity, and because a 64-bit number does not survive a JavaScript client.
display_namestringOutput onlyThe storefront's own name for it, when Plany has read one.
Credit Plany has issued against this link and not been settled for.
Credit held for purchases a player has started and not finished. Counted against the limit, so two players cannot both take the last of it.
The most unsettled credit this link may carry. Set by Plany.
etagstringOutput onlydistributor_share_basis_pointsint32Output onlyThe distributor's share of a sale, out of 10,000. An offer's credit may be at most its price less this, which is how a form can show the largest credit allowed before anybody submits one.
next_page_tokenstringencoresrepeated EncoregamestringRequiredImmutableThe game, in the Game Registry: accounts/{account}/games/{game}.
distributor_linkstringRequiredImmutableThe publisher link that sells this game.
The product on the link's storefront. Must be one of its listings.
DISTRIBUTOR_STEAMproduct_idstringRequiredThe product in the distributor's own terms. For Steam, the app id in decimal — a string because an id is not a quantity, and because a 64-bit number does not survive a JavaScript client.
display_namestringOutput onlyThe storefront's own name for it, when Plany has read one.
FUNDING_VISIBILITY_EXACT_BALANCEFUNDING_VISIBILITY_RUNWAYFUNDING_VISIBILITY_FUNDED_FLAGnamestringIdentifieruidstringOutput onlydisplay_namestringencore_keystringOutput onlyThe handle a game ships to name this Encore without a credential.
Opaque and names no account: it is in every copy of the game, and what it unlocks is reading public funding and starting a purchase — both of which anybody who owns the game may do.
STATE_VERIFYINGSTATE_NEEDS_REVIEWSTATE_VERIFIEDSTATE_DECLINEDSTATE_REVOKEDevidencerepeated EvidencesummarystringWhy the platform did or did not decide on its own, in a sentence.
unknown_signalsrepeated stringSignals that could not be read, by name. A signal that could not be read is not a signal that fell short, and a reviewer needs to know which.
modelstringThe model that read the announcements, when one did.
decided_bystringencore.plany.cloud for an automatic decision, or the person at Plany who reviewed it.
reasonstringThe reviewer's own sentence, for a decision a person made.
decide_timeTimestampprojectstringOutput onlyThe top-up-only project, once there is one.
modeTransfer.ModeMODE_CONVERTMODE_MOVEstateTransfer.StateSTATE_RUNNINGSTATE_SUCCEEDEDSTATE_FAILEDsource_projectstringmessagestringstart_timeTimestampend_timeTimestampetagstringnext_page_tokenstringoffersrepeated Offerdisplay_namestringRequiredShown in the Steam overlay when a player confirms a purchase.
What the player pays, in USD. Steam converts to the player's own currency.
What the project is credited, in USD.
At most the price less the distributor's share. The developer is paid the net and owes Plany the credit, so a credit above the net is a developer owing more than they received on every sale.
namestringIdentifierdescriptionstringactiveboolWhether players can buy it now. Every offer is created active; deactivate one with UpdateOffer.
item_idint64Output onlyThe item id sent to the distributor. Stable for the offer's life.
next_page_tokenstringoffersrepeated PublicOfferoffer_idstringThe offer's own id, to pass to BeginPurchase.
display_namestringdescriptionstringpriceMoneypurchasableboolunavailable_reasonstringpurchasesrepeated PurchasenamestringIdentifierofferstringorder_idstringThe distributor's order id. Decimal, for the reason Listing.product_id is.
player_idstringThe player, in the distributor's terms: a SteamID64.
priceMoneycreditMoneystatePurchase.StateSTATE_INITIATEDSTATE_AUTHORIZEDSTATE_CREDITEDSTATE_CANCELLEDSTATE_FAILEDSTATE_REVERSEDstate_messagestringledger_entrystringThe billing ledger entry that credited the project.
sandboxboolWhether this was a sandbox order, which credits nothing.
create_timeTimestampcredit_timeTimestampreverse_timeTimestampnext_page_tokenstringsettlementsrepeated Settlementnext_page_tokenstringOffer is something a player can buy to fund the servers.
display_namestringRequiredShown in the Steam overlay when a player confirms a purchase.
What the player pays, in USD. Steam converts to the player's own currency.
What the project is credited, in USD.
At most the price less the distributor's share. The developer is paid the net and owes Plany the credit, so a credit above the net is a developer owing more than they received on every sale.
namestringIdentifierdescriptionstringactiveboolWhether players can buy it now. Every offer is created active; deactivate one with UpdateOffer.
item_idint64Output onlyThe item id sent to the distributor. Stable for the offer's life.
PublicFunding is what a game may show its players.
fundedboolbalanceMoneySet only for FUNDING_VISIBILITY_EXACT_BALANCE.
runway_daysint64Whole days, for EXACT_BALANCE and RUNWAY.
Optional, and absent means "no runway to show" — nothing was spent recently to project from, or the visibility hides it. Zero means less than a day left. A plain integer could not tell those apart, and a game showing "0 days left" on a quiet, well-funded server would be telling its players something false and alarming.
visibilityFundingVisibilityFUNDING_VISIBILITY_EXACT_BALANCEFUNDING_VISIBILITY_RUNWAYFUNDING_VISIBILITY_FUNDED_FLAGpurchasableboolWhether a player can buy an offer right now, and why not if they cannot.
unavailable_reasonstringPurchase is one player's order.
namestringIdentifierofferstringorder_idstringThe distributor's order id. Decimal, for the reason Listing.product_id is.
player_idstringThe player, in the distributor's terms: a SteamID64.
priceMoneycreditMoneystatePurchase.StateSTATE_INITIATEDSTATE_AUTHORIZEDSTATE_CREDITEDSTATE_CANCELLEDSTATE_FAILEDSTATE_REVERSEDstate_messagestringledger_entrystringThe billing ledger entry that credited the project.
sandboxboolWhether this was a sandbox order, which credits nothing.
create_timeTimestampcredit_timeTimestampreverse_timeTimestampcandidatesrepeated TransferCandidatetypestringe.g. playpute.plany.cloud/GameServer.
namestringdisplay_namestringlocationstringbelongs_to_gameboolFound through the game itself, rather than only by being in the project.
client_facingboolA shipped game client may address it by name.
movableboolWhether a move of this type is supported. A candidate that is not is listed so the plan can say why CONVERT is not available, never silently left behind.
notestringrecommended_modeTransfer.ModeCONVERT when every billable resource in the project belongs to the game.
MODE_CONVERTMODE_MOVEexplanationstringWhy, in a sentence.
warningsrepeated stringWhat a MOVE would cost the game's players, stated up front: that addresses change, that names in shipped clients stop resolving, that a hibernated server's memory is not carried.
Distributor is a storefront a game is sold through.
| Value | Description |
|---|---|
| DISTRIBUTOR_UNSPECIFIED | |
| DISTRIBUTOR_STEAM | Valve's Steam. Product ids are app ids. |
State of the credential, as last checked against the distributor.
| Value | Description |
|---|---|
| STATE_UNSPECIFIED | |
| STATE_ACTIVE | The key works and owns at least one product. |
| STATE_CREDENTIAL_INVALID | The distributor refused the key. Nothing using it can sell. |
Whether the game has been confirmed as end of life.
| Value | Description |
|---|---|
| STATE_UNSPECIFIED | |
| STATE_VERIFYING | Plany is gathering evidence. |
| STATE_NEEDS_REVIEW | The platform could not decide on its own, and a person will. |
| STATE_VERIFIED | Confirmed. Players can fund the project. |
| STATE_DECLINED | A person at Plany declined, with a reason in `verification.reason`. |
| STATE_REVOKED | Verified once and withdrawn by a person — a game that turned out not to be ending after all. Player purchases stop; credit already bought stays. |
| Value | Description |
|---|---|
| KIND_UNSPECIFIED | |
| KIND_STORE_UNAVAILABLE | The distributor's store no longer sells the product. |
| KIND_OWNERSHIP | The linked credential can act for the product. |
| KIND_ANNOUNCEMENT | The developer's own announcement says the game is ending. |
| KIND_OPERATOR | A person at Plany decided. |
FundingVisibility is how much of a project's funding a game may show players.
Required, with no working zero value. A studio may reasonably want players to see "37 days of servers left" and not want the exact balance of a project on their account published to anybody with the game; guessing either way publishes something nobody decided to.
| Value | Description |
|---|---|
| FUNDING_VISIBILITY_UNSPECIFIED | |
| FUNDING_VISIBILITY_EXACT_BALANCE | The balance and the runway. |
| FUNDING_VISIBILITY_RUNWAY | The runway only, in whole days. |
| FUNDING_VISIBILITY_FUNDED_FLAG | Whether the servers are funded, and nothing else. |
| Value | Description |
|---|---|
| STATE_UNSPECIFIED | |
| STATE_INITIATED | Opened with the distributor; the player has not answered. |
| STATE_AUTHORIZED | The player approved in the overlay; the order is being settled. |
| STATE_CREDITED | Settled, and the project credited. |
| STATE_CANCELLED | The player declined, or never answered. |
| STATE_FAILED | The distributor refused the order. |
| STATE_REVERSED | Refunded or charged back after being credited, and the credit taken back. |
| Value | Description |
|---|---|
| MODE_UNSPECIFIED | |
| MODE_CONVERT | The source project already holds only this game, and becomes the Encore project. Nothing moves and no address changes. |
| MODE_MOVE | A new project is created and each selected resource is moved into it. |
| Value | Description |
|---|---|
| STATE_UNSPECIFIED | |
| STATE_RUNNING | |
| STATE_SUCCEEDED | |
| STATE_FAILED |
| Value | Description |
|---|---|
| STATE_UNSPECIFIED | |
| STATE_PENDING | |
| STATE_RUNNING | |
| STATE_SUCCEEDED | |
| STATE_FAILED | |
| STATE_NEEDS_ACKNOWLEDGEMENT | Waiting for somebody to accept something the move cannot preserve — a stopped server whose disk is gone and can only move as a fresh install. |