generation credit reservation: Follow the reservation and settlement boundaries
A generation credit reservation is the accounting boundary between approving a request and asking an upstream provider to perform work. In the current implementation, it is a real credit deduction recorded with a job, not merely a browser estimate or an expiring authorization displayed in the interface.
Establish what happens together
The job service first looks up the provider, validates parameters, and calculates credits. Invalid model identifiers and schema failures occur before reservation. The database adapter then calls deductForJob and inserts the queued job inside the same database transaction.
The deduction locks the user’s balance row, checks the available amount, consumes eligible source credits, updates the balance, and writes a USAGE entry. If creating the job fails within that reservation transaction, the transaction rolls back its changes. This prevents a successfully committed reservation from being separated from its initial queued record.
An application-side balance preview is useful, but it is not this transaction. Two requests can both see the same preview before either reserves funds.
Follow the external boundary
After reservation commits, the service submits to the provider. The database transaction does not include the upstream provider. The returned reference and upstream model identifier are saved in a later update that marks the job running.
That separation creates an important uncertainty case. If the provider accepts work and saving its reference fails, the request cannot safely be classified as an upstream rejection. The service deliberately does not run the submission-failure compensation path for that later update failure. Preserve the local attempt and investigate acceptance before considering a replacement request.
If provider.submit throws, the service attempts to persist a failed job with UPSTREAM_SUBMISSION_FAILED and then settle its reservation. Its response includes a job identifier for reconciliation. Treat a client timeout by itself more cautiously; it does not prove which internal step occurred.
Interpret settlement
A successful terminal update sets creditsActual to creditsHeld. A failed or canceled terminal update invokes the refund helper and sets actual consumption to zero. The current service uses full reservation or zero for these outcomes rather than partial successful billing.
A submission failure with settlement still pending is recognized by its failed state, submission-failure code, and creditsActual not yet zero. The retry path is specifically for those identifiable pending failures. It is not a blanket historical refund sweep.
Application evidence
Store the approved request, expected reservation, local attempt identifier, returned job identifier, and terminal snapshot. Show reserved and settled amounts with their status rather than calling every debit final consumption.
An offline review should trace validation failure, reservation rollback, accepted submission, explicit submission failure, and failed reference persistence. Approval requires a clear accounting outcome or an explicit unresolved state for each case.
Continue with accepted-job ambiguity, refund evidence, and budget admission when designing the application ledger.
Continue with costs and billing
Where these facts come from
- codebase: src/ai/api/credits.ts
- codebase: src/credits/credits.ts
- codebase: src/ai/jobs/service.ts
- codebase: src/ai/jobs/db.ts
- Editorial keyword hypothesis; search volume not measured