@genex-ai/embed-sdk
v0.22.2
Published
Player identity + durable game state for genex games — signed-in or guest play, per-player save slots, shared world state, and soft-trust leaderboards.
Readme
Genex embed SDK
@genex-ai/embed-sdk connects a game to player identity, saves, commerce and
player-funded generation. Initialize it with your game's slug, API URL and
trusted dashboard origins before using player APIs. See
the play-identity contract
for identity and storage.
Generate during play
Runtime generation requires a signed-in player in a production game and an enabled Genex runtime service. Each player approves their own payment on Genex. The game never handles an OpenRouter key or confirms a charge itself.
import { generate, getGenerationModels } from '@genex-ai/embed-sdk';
// After initEmbed() and player sign-in, populate your model picker from Genex.
const { models } = await getGenerationModels();
const model = models[0];
generateButton.addEventListener('click', async () => {
if (!model) return;
const result = await generate({
modelId: model.id,
estimateCoins: 5, // Fixed price for a started attempt, chosen after benchmarking.
prompt: 'Invent a friendly creature. Return its name and color as JSON.',
outputFormat: 'json',
schema: {
type: 'object',
properties: { name: { type: 'string' }, color: { type: 'string' } },
required: ['name', 'color'],
additionalProperties: false,
},
allowExternal: true,
});
if (result.status === 'succeeded') {
// Validate the shape your game expects before rendering it as data.
showCreature(result.output);
}
});Call generate() directly inside the click handler: a standalone game must
reserve a confirmation popup before asynchronous work. Embedded games ask the
trusted Genex parent to show its modal. Native WebViews and local-test identity
return native_unsupported and local_test_unsupported until they have supported
confirmation surfaces.
GenerateOptions requires estimateCoins (an integer from 1 to 1,000,000 for an API-backed selection), modelId, prompt, outputFormat: 'text' | 'json',
optional bounded schema, allowExternal (default false), idempotencyKey and
timeoutMs (default ten minutes). Reuse an idempotency key only for the same
operation; store it if you need retries to survive a reload. Only models returned
by Genex are accepted. This first adapter generates text and JSON, including
creature descriptions, dialogue, rules and other structured game data.
SDK 0.21.0+ returns the execution status, optional generationId, and
output plus source on success. Cancellation is a normal canceled result,
but it does not imply that consumed work was free. pending with
error: 'wait_timeout' means the SDK stopped waiting; it does not cancel or charge
again. Persist generationId and resume with waitForGeneration(id), or inspect
the current server state with getGeneration(id). Results also carry the
server's billingStatus: 'pending'|'final', chargedCoins,
chargedDisplayUsdCents, reservedCoins and reservedDisplayUsdCents when
available, including on failures. These USD amounts use the frozen coin value;
never derive them from an estimate or the current catalog. An execution can end
while billing remains pending. waitForGeneration() waits for execution;
continue reading getGeneration(id) for later billing settlement. Missing
receipt fields mean unavailable information, not zero cost.
The Genex modal shows the fixed attempt price, its USD equivalent, the game and
the frozen request before approval. New public quotes carry
quote.billingPolicy: 'declared-v1' and kind: 'fixed', with
maxCoins = priceCoins = estimateCoins. Despite its name, estimateCoins
is the developer-declared fixed price of a started attempt: declaring 5 coins
charges 5 even when actual usage would cost 2. Failure, cancellation or reaching
the budget limit after work starts also charges the full price; a usable result
is not guaranteed. No model work means zero charge; an unsuccessful attempt with
verified zero model cost is also uncharged. Every Genex API model
requires a positive price, including Gemini and GLM. Personal funding chosen in
the trusted approval modal remains zero coin.
The provider hard budget is derived from that price after the frozen platform
tariff, then capped by operator limits. Output is clipped to remaining funding;
an input that cannot fit is refused before a call. Unresolved provider expense
keeps billing pending and the reservation held until verified. The trusted UI
must acknowledge the quote's exact billingPolicy at confirmation; an old UI
cannot approve new terms. Previously issued consumed-v1 quotes keep actual-
usage billing and quotes without a policy keep their original promise.
Already-approved zero-price API requests retain their saved terms. An old
unconfirmed zero-price API quote needs a fresh price review before coin approval;
never silently replace its price or start a paid request during recovery.
With allowExternal: true, Genex can offer the configured personal plan matching
the selected model alongside coins: Claude for anthropic/ models, ChatGPT for
openai/ models, and neither for other models. This choice appears only in the
trusted Genex approval modal; never add a "Your Plan" model row in the game.
Each personal choice costs zero coin and its own plan and usage limits apply.
New quotes freeze the matching choices; connector metadata stays server-owned.
Old unapproved quotes are narrowed to the matching provider before confirmation;
approved personal requests retain their connector for recovery. Once chosen,
funding never switches providers or falls back to paid generation. Historical
personal-only offerings never gain free coin execution. Genex neither collects
nor hosts subscription credentials.
source: 'external' means user-supplied output, not proof that a particular model
generated it; modelProvenance: 'unverified' makes that explicit. Treat every generated output as data. Never execute returned code
or use a claimed model/source as authority to mint coins, rewards or items.
Registered generation workflows
SDK 0.21.0+ supports multi-step generation and usage receipts through an
operator-registered workflow. This requires the game's trusted server executor;
an ordinary creator API key cannot register a workflow or dispatch its steps.
generate() and requestWorkflow() both require the fixed estimateCoins
price and use the declared-attempt policy above.
import { getWorkflowOfferings, requestWorkflow, getEmbedToken } from '@genex-ai/embed-sdk';
// Load from this game's registered workflow before enabling the model picker.
const catalog = await getWorkflowOfferings(configuredWorkflowId);
// Public offerings contain paid API/model rows.
// Personal plan funding is offered only by the trusted Genex approval modal.
// Keep the complete selected offering: availability, funding, model and prices.
renderModelPicker(catalog.offerings);
createButton.addEventListener('click', async () => {
// Save the selected offering, exact input and benchmarked fixed price together.
const { offeringId, input, estimateCoins, idempotencyKey } = pendingCreation;
const approval = await requestWorkflow({
workflowId: catalog.workflowId, offeringId, input, estimateCoins, allowExternal: true, idempotencyKey,
});
if (approval.generationId) saveGenerationId(approval.generationId);
if (approval.status !== 'authorized' || !approval.generationId) return;
// Application-specific endpoint: the game backend verifies and claims the
// approved Genex run, binds it to one durable job, and enqueues its executor.
await enqueueApprovedWorkflow({
generationId: approval.generationId, embedToken: getEmbedToken(), input,
});
});Call requestWorkflow() directly from the click, before an earlier await, so
the SDK can reserve the standalone popup. It returns approval, not the
finished creature: { status: 'authorized'|'canceled'|'expired'|'failed'|'pending',
generationId?, funding?, error? }, plus available billing receipt fields.
Waiting for completion before the backend
claims and enqueues the run would deadlock. Authorized replay also covers an
already-completed run, allowing the backend to recover the same job or artifact.
Personal-provider instructions remain open after authorization.
input is { operation: 'create'|'refactor', description, bundleId, creatureId?,
revisionDigest? }; refactors require both creature identity and its revision
digest. Persist this input, offering and idempotency key before submission; reuse
them only for the same operation after sign-in or reload. Persist the returned
generation ID. Read with getGeneration(id) and wait with
waitForGeneration(id) after enqueueing. A wait timeout does not cancel work.
Recovering local state never starts a fresh charge without another player click.
Read a fresh embed token when sending an approved run to the game backend; never
persist or log the token.
The catalog's estimatedCoins and priceCoins are suggested price aliases,
and its maximum describes operator limits. Choose the game's fixed attempt price
after development benchmarks, pass it as estimateCoins, and show coin plus USD.
The player's final approval always uses the server quote. Preserve the selected
model, bundle, funding, availability and operator-owned tariff; the game cannot
change provider prices, dispatch costs or settlement.
For new public workflow quotes, maxCoins is the same fixed amount as
priceCoins, not a promise to bill actual usage. The operator-owned multiplier
(2 or 3, default 3) determines how much model work that fixed price can fund.
The whole started attempt is charged once, including failed or canceled work;
no model work is zero and unknown expense keeps the hold pending. Only the
selected model is admitted. The trusted executor validates and delivers the
artifact; the browser cannot settle it.
Use resumeWorkflow(generationId, { timeoutMs? }) directly from a click to
reopen a saved approval. It reads the original request without creating a quote
or requiring a new price, including pre-0.21 consumed and legacy approvals.
Use requestWorkflow() with required estimateCoins for new operations.
Legacy quotes without billingPolicy retain their original fixed-price and
refund terms. Never replace a saved quote's policy with the current catalog's
policy, infer a missing multiplier, or reprice an approved operation.
All API-backed Genex offerings are paid, including google/gemini-3.8-flash and
z-ai/glm-5.3-flash. Airena's free access to those models is game-funded through
its own direct OpenRouter path, outside the Genex runtime API.
Personal Claude/ChatGPT funding choices in the Genex approval modal cost
0 coins and use the player's own account through Genex-owned
MCP connectors. Claude uses /player/mcp; ChatGPT uses /player/chatgpt/mcp.
ChatGPT connector availability depends on account and workspace policy. One
external request may be active per player across both providers. Follow the
trusted instructions to fetch and submit each exact stage and attempt; no
subscription credentials pass through the game and no paid fallback is allowed.
Submitted results remain user-supplied; the executor must validate them before
delivery. Both APIs restrict personal funding to the selected model's provider.
These workflow endpoints still require signed-in production play, including personal funding. Airena's sponsored direct guest path is separate; do not weaken the workflow's player/session checks to reproduce it.
The runtime API contract describes both APIs, executor authorization, recovery and connector endpoints.
Benchmark your own coin costs on the server
SDK 0.21.0+ provides @genex-ai/embed-sdk/development. Use it only in a
local Node script or trusted server with your own full creator bearer credential
and an owned projectId. It needs no production play token and cannot choose a
player wallet. Restricted API keys and personal-only offerings are refused.
Keep the credential out of game code, browser environment variables and logs;
the subpath is disabled for browser resolution and rejects browser execution.
maxCoins is an explicit positive maximum from your own wallet. Development
uses consumed-v1: actual model usage at the frozen normal tariff, including
failed usage, up to that maximum. Gemini/GLM models use this normal paid tariff
too. Actual zero cost is zero coin;
unknown expense remains held. No personal or paid fallback occurs.
import { developmentGenerate } from '@genex-ai/embed-sdk/development';
const receipt = await developmentGenerate({
apiUrl: process.env.GENEX_API_URL!,
creatorToken: process.env.GENEX_CREATOR_TOKEN!,
projectId: process.env.GENEX_PROJECT_ID!,
maxCoins: 5,
request: {
modelId: 'your-configured-model-id',
prompt: 'Invent a friendly creature name.',
outputFormat: 'text',
idempotencyKey: 'calibration-sample-001',
},
});
// A 5-coin maximum can return chargedCoins: 2. Use repeated samples to choose
// the public fixed estimateCoins price; public fixed 5 still charges 5.
if (receipt.billingStatus === 'final') useForCalibration(receipt.chargedCoins, receipt.usage);The full receipt includes execution status, output on success, charged/reserved
coins and USD, plus development-only usage: { costUsdPicos, costUsd,
maxProviderUsdPicos, unknownProviderUsdPicos }. Unknown cost is null rather
than zero; workflow cost aggregates known actual step costs and reports remaining
unknown exposure separately. No token counts are inferred.
For a registered workflow, call developmentRequestWorkflow({ apiUrl,
creatorToken, projectId, workflowId, maxCoins, request: { offeringId, input,
idempotencyKey? } }). It returns the accepted, claimed workflow immediately so
your trusted executor can enqueue it. After enqueueing, call
waitForDevelopmentGeneration(config, id, timeoutMs?). Both generic and workflow
benchmarks are recoverable with getDevelopmentGeneration(config, id) and
cancelDevelopmentGeneration(config, id). The wait ends at execution plus final
billing, or returns the latest server view at timeout; timeout does not cancel
or claim a refund. Save the operation ID and idempotency key for recovery.
