NodaroAccount
Nodaro Docs
DocumentationNode ReferenceModelsTemplatesAI Agents (MCP)DevelopersSelf-hostingResearchCompare

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.

The billing account's tools

The billing account manages the deployment's pool on its own page, /billing-admin, signed in with its own session.

Auto top-up of the pool

The /billing-admin page can turn on auto top-up. When the pool drops below a threshold, in raw credits, the card saved by the billing account's last card purchase is charged automatically.

  • The usual limits apply. Each charge is from $5 to $1,000. There are at most 3 charges a day, 10 minutes apart, and auto top-up stops after 3 failed charges.
  • Only work started in the product itself can trigger a charge. Personal API tokens, OAuth apps, connected instances, webhook, schedule and Telegram triggers spend the pool, but never charge the card. A pool they drain below the threshold is topped up on the next generation started in the app.
  • A customer's own card is never charged. Auto top-up is for the billing account's pool only.
  • Read the status. GET /v1/deployment-billing/balance includes autoRecharge: whether it is enabled and active, the threshold, the amount, the failures and the last attempt. autoRecharge is null when the status cannot be read.

Auto top-up needs a deployment that takes card payments. Without one, the deployment's operator tops up the pool instead.

Billing integration keys

The /billing-admin page also creates billing integration keys, for another system such as your back office. A key reaches only /v1/deployment-billing/*. Its kind is fixed when it is created:

KeyPrefixWhat it may do
Fullndr_bill_…Read the pool, the usage and the prices, and manage allowances.
Read-onlyndr_bill_ro_…Read only. Any request other than GET answers 403 billing_key_read_only, before the route runs.

Use a read-only key wherever only monitoring is needed.

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 model's price key for each operation, such as describe-to-picker:<model>.
estimatetrue on a text model's row and on each such pricing entry: the figure is an estimate.
denominationThe credit unit and the display conversion.
purchaseOptionsThe standard top-ups in USD, with their effective price per credit. Separate commercial terms may apply.
  • Text models are billed per model. A text model's creditIdentifier is its Generate Text price key, llm-chat:<model>, with :direct when the default call runs on the model maker's own API. Its creditCost is an estimate: a typical call, charged its actual usage per token. Show it as approximate, ≈N. Workflow Copilot, Content Recipe and Content Ideas still bill a tier row, and carry no estimate.
  • A text model with no price on its lane is listed in missing. A text model's isEnabled and category come from the llm-chat row.
  • Media variants 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

What is an external wallet in Nodaro?

A service you run that holds each customer's shared budget across your products. A dedicated Nodaro Cloud deployment asks it to reserve credits before a generation runs, settles the actual amount afterwards, and reads the balance to show it.

Which Nodaro deployments can use an external wallet?

A dedicated Nodaro Cloud deployment with a billing account, the one account that pays for every action on the deployment. Its customers sign in through the deployment's SSO provider, which identifies them to the wallet.

What happens if the wallet does not answer in time?

Nothing is generated. A timeout, a malformed answer, a mismatched amount or id, a missing identity and a denial all stop the request before any model runs.

Can the same settle request arrive twice?

Yes. Delivery is at least once, so your wallet must return the original result for a repeated settle without charging again. The Idempotency-Key header identifies repeats.

Do reservations expire?

No. A generation and a human review can take longer than any HTTP request, so a hold stays until Nodaro settles it. Never release an active hold because a callback is late.

Can a billing integration key be read-only?

Yes. The billing account creates full keys (ndr_bill_…) and read-only keys (ndr_bill_ro_…) on its /billing-admin page. A read-only key may only send GET requests; any other request answers 403 billing_key_read_only.

Last updated on

On this page