End-to-end generated client
Configure Console, publish runtime state, generate a client, call Valqio from app code, and inspect the evidence.
This guide shows the full public integration loop for server-side product work. Use it when a Feature, Usage Control, or Limit should generate an app-native helper instead of exposing raw control keys in product code.
The guide follows a focused sequence: publish the target environment, generate its client, call Valqio before billable work, then inspect evidence.
1. Model the control and app integration
In Console:
- Select the project and environment your app will call.
- Create or open the Feature, Usage Control, or Limit that should decide whether work may run.
- Under Use in your product, enter a product resource and verb, such as
Workspaces / Create. - Confirm the derived action key (
workspaces.create), generated SDK path (customer.actions.workspaces.create), and lifecycle. - Attach the owning control to the relevant plan or customer policy.
The resource and verb should name product work, not Valqio infrastructure. Good
pairs include Workspaces / Create, Videos / Render, API / Call,
Invoices / Send, and Text / Generate.
2. Publish the environment
Open Publish in Console and preview the runtime state. Confirm the app integration, owning control, plan, and customer policy counts look right.
Then publish the environment. Publishing is what lets the hosted data plane and generated client serve decisions from the same environment state.
3. Create a runtime key
Open Developers in Console and create a server runtime key for the same environment.
Store these values as backend secrets:
VALQIO_API_KEY=<server runtime key>
VALQIO_DATA_PLANE_URL=<hosted runtime API URL>
VALQIO_PROJECT_ID=<project id>
VALQIO_ENVIRONMENT=<environment key>Do not expose server runtime keys in browser code.
4. Get SDK tooling access
@valqio/sdk-node and @valqio/cli are currently private-preview packages and are not published on the public npm registry. Request access from Valqio, then configure the registry details supplied with your account.
After access is configured, install the Node SDK in your server app:
npm install @valqio/sdk-nodeUse the generated-client command or file shown in Console. After CLI access is enabled for your account, the command will look like this:
npx @valqio/cli schema generate \
--from applied \
--project <project-id> \
--env production \
--out app/server/valqio.generated.tsGenerated client files come from Console or Valqio's SDK tooling for your project. Use the exact Console command for your project and sign in with your Valqio account if the CLI asks for authentication.
5. Configure the generated client
import { createMyAppValqioClientFromEnv, inspectMyAppValqioReadiness } from './valqio.generated'
async function createReadyMyAppClient() {
const bindings = inspectMyAppValqioReadiness()
if (!bindings.ready) {
throw new Error(`Valqio bindings unresolved: ${bindings.unresolved.join(', ')}`)
}
const myApp = createMyAppValqioClientFromEnv({
runtimeOverrides: {
mode: 'remote-only',
},
})
const runtime = await myApp.ready()
if (!runtime.snapshotReady) {
throw new Error('Valqio published runtime snapshot is unavailable')
}
return myApp
}
export const myAppPromise = createReadyMyAppClient()The synchronous inspector validates generated bindings. The asynchronous ready() call checks whether the runtime client can obtain a published snapshot. After the first load, it may use the client's cached snapshot, so treat it as snapshot readiness rather than a general data-plane health probe. Fail fast on either setup check instead of running billable product work without enforcement.
6. Call Valqio before work runs
For in-process work that should reserve value before it starts, use the
generated callback-owning run() method:
const requestId = 'req_runtime_01'
const customerId = 'workspace_acme'
export async function handleCreateWorkspace() {
const myApp = await myAppPromise
const customer = myApp.customer({ id: customerId }, { requestId })
const workspace = await customer.actions.workspaces.create.run(
{
quantity: 1,
idempotencyKey: `createWorkspace:${requestId}`,
context: { endpoint: '/workspaces' },
},
async ({ transactionId }) => createWorkspace({ transactionId }),
)
return { status: 200, body: { workspace, requestId } }
}run() begins and claims the transaction before invoking the callback. It
commits successful reservable work, releases failed work, and never releases
after successful work if commit remains uncertain. A denial throws
ValqioActionDeniedError before createWorkspace() runs.
For immediate work, choose one generated action or its underlying meter enforce helper; do not call both for the same unit. This example uses the action and keeps the same request identity pattern:
export async function handleSendInvoice(customerId: string, requestId: string) {
const myApp = await myAppPromise
const customer = myApp.customer({ id: customerId }, { requestId })
const invoice = await customer.actions.invoices.send.run(
{ idempotencyKey: `sendInvoice:${requestId}` },
async () => sendInvoice(),
)
return { status: 200, body: { invoice, requestId } }
}7. Inspect decision evidence
Open Decisions → History in Console and search by request ID, subject, action key, control, meter, or outcome.
Confirm:
- the subject is the expected customer or workspace
- the action or meter matches the generated helper
- the decision happened before product work ran
- the idempotency key is stable for retries
- the deny reason is product-specific enough for your app response
- the reservation ID is present for reservation flows
8. Trace Runtime Activity
Open Runtime Activity and check the lifecycle:
- reserve created before work started
- commit recorded after successful work
- release recorded after failed or cancelled work
- repeated settlement calls resolved the same action transaction
- support trace identifiers match app logs
Runtime Activity is the proof that the usage transaction lifecycle settled correctly.
9. Review billing sync
If billing sync is enabled, open Draft Invoices for the customer and period.
Review:
- usage source context
- recent usage by meter
- exportable lines
- blocked or failed lines
- selected Stripe connection
- Provider IDs returned by Stripe and export evidence after export
Billing providers invoice and collect payment. Valqio records the runtime evidence and usage outcomes that explain what should be synced.
Production checklist
- The app uses the hosted data plane URL from Console.
- The runtime key belongs to the same project and environment as the generated client.
- The generated client was refreshed after publishing integration changes.
- Every billable request sends
requestIdand a stable idempotency key. - The app handles deny without running product work.
- Reservation flows commit after success and release after failure.
- Support can find evidence from request ID without seeing secrets.