How the Reservation-Based Generation Flow Prevents Credit Loss in Awesome GPT Image 2

The reservation-based generation flow prevents credit loss by holding credits in a pending state during image generation, then either finalizing the deduction upon success or automatically refunding the user upon failure.

The freestylefly/awesome-gpt-image-2 repository implements a transactional safety mechanism that ensures users never lose credits for failed image generations. By wrapping every request in a database-backed reservation lifecycle, the system guarantees that free-generation quotas and credit balances are only permanently consumed when the upstream image model successfully returns a result.

The Four-Step Reservation Lifecycle

The application orchestrates every generation request through a strict reserve → generate → complete/release workflow implemented in api/generate-image.js and enforced by PostgreSQL stored procedures.

Step 1: Reserve

Before calling any external image service, the handler invokes reserveGeneration (or reserveCreditGeneration for administrators). This function executes the reserve_generation_usage stored procedure, which:

  • Creates a row in the generation_reservations table with status = 'pending'
  • Provisionaly decrements the user's available free generations or holds the required credit amount
  • Returns a unique reservationId that tracks this specific request

According to the source code in supabase/migrations/202605090001_user_credits.sql, this reservation acts as a lock on the resources, preventing race conditions where multiple simultaneous requests might overdraft the user's balance.

Step 2: Generate

Only after obtaining a valid reservation does the handler call the external generateImage service. This ordering is critical: if the upstream service fails, times out, or returns a 429 error, the reservation record persists in the database, enabling a full rollback.

Step 3: Complete on Success

When the image generation succeeds, the handler calls completeReservation, which executes the complete_generation_reservation stored procedure (lines 74-88 in the migrations file). This procedure:

  • Verifies the reservation status is still pending
  • Updates the status to succeeded
  • Records the completion timestamp

No credit adjustments occur during completion. The deduction was already applied during the reserve step; completing the reservation simply finalizes the transaction, preventing double-charging or duplicate refunds.

Step 4: Release on Failure

If any error occurs during generation, the handler invokes releaseReservation, triggering the release_generation_reservation stored procedure (lines 92-145). This critical rollback logic:

  • Checks that the reservation remains in pending status to prevent double-refunds
  • For free generations: increments free_generations_used to restore the quota
  • For credit payments: adds the credit amount back to credit_balance and records a refund transaction
  • Marks the reservation as failed with an error code

By explicitly rolling back provisional changes in the database, the system ensures zero credit loss for failed requests.

Key Implementation Details

Database Schema and Stored Procedures

The generation_reservations table serves as the source of truth for all in-flight requests. Three core stored procedures manage the state transitions:

  • reserve_generation_usage: Atomically checks balance, creates the pending reservation, and holds resources
  • complete_generation_reservation: Marks successful completions without modifying credit totals
  • release_generation_reservation: Handles the refund logic by restoring free_generations_used or credit_balance

These procedures are defined in supabase/migrations/202605090001_user_credits.sql and enforce consistency at the database level, independent of application logic.

API Handler Logic

The api/generate-image.js file implements the orchestration layer:

  • Lines 59-80: Reserve the generation before any external calls
  • Lines 21-23: Complete the reservation after successful image retrieval
  • Lines 36-38: Release the reservation and trigger refunds on any caught exceptions

This structure ensures that even if the Node.js process crashes between the external API call and the finalization step, the database maintains a record of the pending reservation that can be cleaned up by background jobs or investigated for manual refund.

Code Examples

Reserving a Generation

// api/generate-image.js
const reservation = auth.profile.isSuperAdmin
  ? await reserveCreditGeneration(auth.client, auth.user.id, caseId, prompt)
  : await reserveGeneration(auth.client, auth.user.id, caseId, prompt);

// reserveGeneration calls the Supabase RPC:
// SELECT * FROM reserve_generation_usage(user_id, case_id, prompt)

Completing a Successful Reservation

// After successful image generation
await completeReservation(auth.client, reservation.reservationId);

// Executes: SELECT complete_generation_reservation(reservation_id)
// Updates status from 'pending' to 'succeeded'

Releasing and Refunding on Failure

// When upstream returns 429 or other errors
const errorCode = error?.status === 429 ? 'UPSTREAM_BUSY' : 'GENERATION_FAILED';
await releaseReservation(auth.client, reservation.reservationId, errorCode);

// Executes release_generation_reservation, which:
// 1. Restores free_generations_used OR adds credits back to credit_balance
// 2. Inserts a refund transaction record
// 3. Sets status = 'failed'

Summary

  • The reservation pattern creates a pending transaction state before consuming external resources
  • Database-level stored procedures enforce atomic operations for reserving, completing, and releasing credits
  • Automatic rollback via release_generation_reservation restores both free-generation quotas and credit balances when upstream services fail
  • The implementation in api/generate-image.js ensures every code path either completes or releases the reservation, eliminating orphaned transactions

Frequently Asked Questions

What happens if the server crashes during image generation?

The reservation row remains in the generation_reservations table with status = 'pending'. Because the credit was already deducted during the reserve step but the generation never completed, administrators can identify these stale reservations and manually trigger refunds, or a background cleanup job can automatically release reservations older than a specific threshold.

How does the system prevent double-charging users?

The complete_generation_reservation procedure only updates the reservation status without touching the credit_balance or free_generations_used columns. Since the deduction occurred atomically during the reserve step, and completion merely changes a status flag, there is no code path that subtracts credits twice for a single reservation.

Can administrators bypass the free-generation quota?

Yes. The codebase provides reserveCreditGeneration specifically for super administrators (lines 59-80 in api/generate-image.js). This function forces credit usage even when free generations are available, ensuring that admin usage is always logged and billed against credits rather than consuming the limited free tier.

What error codes trigger a refund?

Any exception caught in the generation handler triggers releaseReservation. Specifically, HTTP 429 responses map to the UPSTREAM_BUSY error code, while all other failures use GENERATION_FAILED. Both codes result in identical refund behavior through the release_generation_reservation stored procedure.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →