Nodaro Docs
DocumentationNode ReferenceModelsAI Agents (MCP)DevelopersSelf-hostingResearch

External wallets

Connect a dedicated Nodaro Cloud deployment to your own shared wallet, which reserves, settles and reports each customer's credits across all your products.

Available on Nodaro Cloud

An external wallet is a service you run that holds each customer's shared budget, and that a dedicated Nodaro Cloud deployment consults before it spends. Nodaro asks the wallet to reserve credits before each generation, settles the exact amount afterwards, and reads the balance to show it to the customer. The deployment's billing account still pays for platform usage from its prepaid pool; the wallet decides how much of that each customer may use, across all your products.

How it fits together

  • A billing account pays. The deployment names one billing account (billing.payerAccount in its surface profile), whose prepaid pool pays for every action on the deployment. See Editions and surface profiles.
  • SSO identifies the customer. Customers sign in through the deployment's SSO provider. The provider's subject for a customer, sent as sso_subject, is how your wallet recognizes the customer.
  • The wallet decides. For every single-node request and every workflow node, Nodaro checks the billing account's prepaid pool, then asks your wallet to reserve against the customer's shared budget. The customer's local Nodaro credit balance plays no part.

An empty prepaid pool can stop a request before the wallet is asked. Once asked, the wallet's decision is final for the customer's shared budget.

Reserve

A customer starts a generation. Nodaro sends reserve with an operation_id and an amount, and your wallet holds that amount atomically and answers allow or deny.

Run

On allow, the model runs. On anything else, nothing is sent to the model.

Settle

When the work ends, Nodaro sends settle with the actual amount. Your wallet charges exactly that amount and releases the rest of the hold.

Show the balance

The studio asks your wallet for the customer's balance, and shows it in the deployment's display unit.

Configure the deployment

Set these variables on the API and on the workers:

VariableWhat it sets
DEPLOYMENT_WALLET_URLThe wallet's endpoint. It must use HTTPS.
DEPLOYMENT_WALLET_TOKENA dedicated server secret that Nodaro sends as a bearer token.
DEPLOYMENT_WALLET_SSO_PROVIDERThe id of the SSO provider whose subjects identify customers.
DEPLOYMENT_WALLET_TIMEOUT_MSThe time limit of each wallet call, from 1000 to 30000 milliseconds. The default is 10000.
  • Set the first three together. Leave all three unset to keep the deployment's existing billing.
  • Turn off local allowances. The per-user allowance enforcement, billing.allowances: "enforce", must be off. A configuration that combines the two is refused at activation.
  • Agree on the unit first. Before you activate the wallet, agree on the conversion between Nodaro credits and your product's budget. Set billing.unitRate and billing.unitLabel in the surface profile to match, so both products show the same figure.

The request contract

Nodaro sends every call as a POST with a JSON body that carries contract: 1 and unit: "nodaro_credit", and the header Authorization: Bearer <token>. The amounts are Nodaro credits. Never read them as your own product's units without an explicit conversion that your wallet owns.

Reserve

{
  "contract": 1,
  "unit": "nodaro_credit",
  "action": "reserve",
  "operation_id": "a3354131-4e5e-43a6-8f61-3914f09c658e",
  "job_id": null,
  "user_id": "5bf0d884-47b1-468e-a7b2-2433f957b267",
  "sso_provider": "partner",
  "sso_subject": "customer-123",
  "model_identifier": "example-model",
  "reserved_credits": 30
}
FieldMeaning
operation_idOne usage record. A job or a workflow can contain several.
sso_subjectThe customer. It comes from trusted sign-in data that Nodaro's server manages, so use it to identify the customer.
sso_provider, user_idThe SSO provider and the Nodaro user behind the request.
model_identifierThe model being billed.
reserved_creditsThe whole number of credits to hold.

Reserve the amount atomically against the customer's shared available budget before you answer. To allow the request:

{"contract":1,"unit":"nodaro_credit","operation_id":"a3354131-4e5e-43a6-8f61-3914f09c658e","decision":"allow","reserved_credits":30}

For insufficient funds or an account that may not spend, answer HTTP 200 with the same envelope and "decision": "deny"; no amount is needed. Answer a service error with a status outside the 2xx range.

Nothing reaches the model unless your wallet allows it. A timeout, a malformed answer, a mismatched amount or id, a missing identity and a denial all stop the request. The request's Idempotency-Key header is <operation_id>:reserve.

Settle or release

When the work ends, Nodaro sends the same operation fields with "action": "settle" and actual_credits. Charge exactly that amount and release the rest of the hold. A settle of 0 releases everything.

{"contract":1,"unit":"nodaro_credit","operation_id":"a3354131-4e5e-43a6-8f61-3914f09c658e","settled":true,"actual_credits":12}
  • The key is <operation_id>:settle. Delivery is at least once. A retry must return the original result without charging again.
  • Refuse conflicts. Reject a request for the same operation with a different identity, reservation ceiling or final amount.
  • A settle can arrive before a delayed reserve. A settle of 0 must create a permanent final record, even for an operation you do not know yet. A later reserve must never reopen it.
  • Holds never expire. A generation, and a human review of its result, can outlive any HTTP request. Keep each hold until it is settled, and keep your idempotency records. Never release an active hold because a callback is late.

Balance

{"contract":1,"unit":"nodaro_credit","action":"balance","user_id":"5bf0d884-47b1-468e-a7b2-2433f957b267","sso_provider":"partner","sso_subject":"customer-123"}
{"contract":1,"unit":"nodaro_credit","sso_subject":"customer-123","available_credits":250}
  • Available means after holds. available_credits excludes every outstanding hold, across all your products.
  • Fractions are allowed here. The balance may be fractional, so a remainder smaller than one billable credit can still be shown. Reserve and settle amounts are always whole credits.
  • One figure in the studio. The studio converts the balance once into the deployment's display unit and hides its own per-user allowance display. An unavailable balance appears as unknown.
  • Reserve decides races. When requests arrive at the same moment, the reserve call, not the balance, is authoritative.

Delivery and recovery

  • Nodaro tries to deliver each settlement at once. If that fails, a worker that runs every minute retries it, with a backoff of up to one hour. Unacknowledged settlements are kept until they are delivered.
  • Nodaro cancels on its own only the reservations that were never authorized. Authorized jobs that are running follow the normal job recovery.
  • Never remove the wallet configuration, or point it to another wallet, while operations remain open. To rotate the token, rotate it on the same wallet.

Price discovery

GET /v1/deployment-billing/pricing returns the deployment's price list for your wallet's own display and planning. It needs the billing account's session or one of its billing integration keys.

FieldMeaning
creditCostThe base tariff of a model.
creditIdentifierThe identifier that is billed.
pricingBasisWhat the base tariff assumes.
pricingFor text models, the identifiers of each operation. Text models are listed as llm-chat at default settings.
denominationThe credit unit and the display conversion.
purchaseOptionsThe standard top-ups in USD, with their effective price per credit. Separate commercial terms may apply.
  • Media variants and text operations resolve through the prices in effect. A variant without a configured price stays null, with an error code.
  • Settings, quantity, duration and measured usage can change the final charge. Account with the reserve and settle amounts, never with a base tariff from this list.
  • To detect changes, prefer a full request with If-None-Match. The since parameter follows the timestamps of price rows, and cannot see every change of runtime configuration.

Frequently asked questions

Last updated on

On this page