# How to Reserve Generation Credits Before Submitting to APIMart

> Learn how to reserve generation credits for APIMart by calling reserve_generation_usage. Ensure atomic credit handling and prevent overdrafts before submitting your requests.

- Repository: [苍何/awesome-gpt-image-2](https://github.com/freestylefly/awesome-gpt-image-2)
- Tags: how-to-guide
- Published: 2026-09-09

---

**The system reserves generation credits by calling the Supabase stored procedure `reserve_generation_usage` to create a pending reservation row before invoking APIMart, ensuring atomic credit handling and preventing balance overdrafts.**

In the `freestylefly/awesome-gpt-image-2` repository, the platform implements a defensive credit reservation pattern to protect user balances during third-party API calls. This workflow guarantees that credits are only consumed after successful APIMart job submission, while handling failures gracefully through rollback mechanisms.

## The Three-Layer Reservation Architecture

The reservation system operates through three coordinated components: a PostgreSQL stored procedure for atomic database operations, a Node.js wrapper for error translation, and the HTTP handler for orchestration.

### Supabase Stored Procedure

The core logic resides in [`supabase/migrations/20260509090000_membership_billing.sql`](https://github.com/freestylefly/awesome-gpt-image-2/blob/main/supabase/migrations/20260509090000_membership_billing.sql), specifically within the `reserve_generation_usage` function (lines 280–306). This **PostgreSQL stored procedure** accepts four parameters:

- `p_user_id` – The UUID of the requesting user
- `p_case_id` – Integer identifier for the generation context
- `p_prompt` – The text prompt being submitted
- `p_force_credit` – Boolean flag allowing administrators to bypass free generation allowances

The procedure inserts a row into `public.generation_reservations` with status `pending` and calculates the `credit_amount` based on the user's remaining free generations. It returns a `reservation_id` UUID that tracks the transaction throughout its lifecycle.

### Node.js Wrapper Function

The `reserveGeneration` function in [`api/generate-image.js`](https://github.com/freestylefly/awesome-gpt-image-2/blob/main/api/generate-image.js) (lines 30–48) serves as the **RPC client wrapper**. It translates database errors into HTTP semantics—specifically converting Supabase exceptions containing `CREDITS_REQUIRED` into a `402 Payment Required` response.

```javascript
// src/api/generate-image.js – reserveGeneration
export async function reserveGeneration(client, profile, userId, caseId, prompt) {
  const { data, error } = await client.rpc('reserve_generation_usage', {
    p_user_id: userId,
    p_case_id: caseId,
    p_prompt: prompt,
    p_force_credit: Boolean(profile?.isSuperAdmin)
  });

  if (error) {
    const msg = String(error.message || error.details || '').toUpperCase();
    if (msg.includes('CREDITS_REQUIRED')) {
      const e = new Error('CREDITS_REQUIRED');
      e.code = 'CREDITS_REQUIRED';
      throw e;
    }
    throw error;
  }

  const row = Array.isArray(data) ? data[0] : data;
  if (!row?.reservation_id) throw new Error('RESERVATION_FAILED');
  return { reservationId: row.reservation_id };
}

```

### Handler Orchestration

The HTTP handler coordinates between credit reservation and APIMart submission. After obtaining the `reservationId`, it immediately calls `submitApimartGeneration` from [`api/_lib/apimart.js`](https://github.com/freestylefly/awesome-gpt-image-2/blob/main/api/_lib/apimart.js), then updates the reservation row with the third-party task identifier.

## Step-by-Step Implementation

Implementing the reservation flow requires sequential database operations with strict error handling at each phase.

### Creating the Reservation

First, invoke the stored procedure through the authenticated Supabase client:

```javascript
reservation = await reserveGeneration(
  auth.client,
  auth.profile,
  auth.user.id,
  caseId,
  prompt
);

```

This creates the `generation_reservations` row with status `pending`, effectively locking the user's credit balance without finalizing the deduction.

### Handling Insufficient Credits

When the stored procedure detects insufficient credits (and `p_force_credit` is false), it raises an exception. The wrapper catches this and throws a coded error that translates to HTTP 402:

```javascript
// Results in 402 response to client
if (msg.includes('CREDITS_REQUIRED')) {
  const e = new Error('CREDITS_REQUIRED');
  e.code = 'CREDITS_REQUIRED';
  throw e;
}

```

### Linking the APIMart Task

Upon successful reservation, submit to APIMart and persist the external task ID:

```javascript
// Submit to APIMart
const submitted = await submitApimartGeneration({
  apiKey: config.apiKey,
  prompt,
  language: body.language,
  webhook: `${appUrl}/api/generation`
});

// Link the APIMart task to the reservation
await auth.client
  .from('generation_reservations')
  .update({
    provider: 'apimart',
    provider_task_id: submitted.taskId
  })
  .eq('id', reservation.reservationId)
  .eq('user_id', auth.user.id);

```

## Error Handling and Rollback Mechanisms

If the `submitApimartGeneration` call throws an exception, the handler must release the reservation to prevent orphaned locks on the user's credit balance. The system invokes the `release_generation_reservation` RPC to mark the reservation as failed and restore the pending credit allocation.

This **compensating transaction** pattern ensures that users are never charged for generations that fail to reach APIMart, while the `pending` status in the reservation table enables audit trails for debugging failed submissions.

## Summary

- **Atomic credit protection**: The `reserve_generation_usage` procedure creates a `pending` row in `generation_reservations` before any third-party API calls, preventing race conditions and overdrafts.
- **Error translation layer**: The `reserveGeneration` wrapper in [`api/generate-image.js`](https://github.com/freestylefly/awesome-gpt-image-2/blob/main/api/generate-image.js) converts database errors into appropriate HTTP status codes, specifically `402` for credit deficiencies.
- **Task correlation**: After APIMart accepts the job, the system stores the `provider_task_id` in the reservation row, linking internal credit tracking to external task identifiers.
- **Automatic rollback**: Failed APIMart submissions trigger `release_generation_reservation`, ensuring credits remain available for retry attempts.

## Frequently Asked Questions

### What happens if APIMart submission fails after reservation?

The handler catches the exception from `submitApimartGeneration` and calls the `release_generation_reservation` RPC. This marks the reservation as failed and releases the held credits back to the user's available balance, maintaining consistency between the platform's credit ledger and actual usage.

### How does the system handle concurrent generation requests?

The `reserve_generation_usage` stored procedure executes as a **SECURITY DEFINER** function within PostgreSQL, providing atomic isolation. When multiple requests arrive simultaneously, each creates a separate `pending` reservation row. The credit calculation occurs within the same transaction as the insert, preventing overspending scenarios even under high concurrency.

### Can administrators bypass credit requirements?

Yes. The `p_force_credit` parameter in the stored procedure, passed via `profile.isSuperAdmin`, allows administrators to reserve generations regardless of their credit balance. When this flag is true, the system reserves the generation but marks it as forced, enabling supervisory overrides while maintaining audit records in the `force_credit` column.

### Where is the reservation schema defined?

The `generation_reservations` table schema and the `reserve_generation_usage` function are defined in [`supabase/migrations/20260509090000_membership_billing.sql`](https://github.com/freestylefly/awesome-gpt-image-2/blob/main/supabase/migrations/20260509090000_membership_billing.sql). This migration establishes the credit reservation infrastructure alongside membership and billing tables, ensuring referential integrity between reservations, user profiles, and case records.