# How the Webhook System Handles Async Generation Results in Awesome GPT‑Image‑2

> Discover how the Awesome GPT-Image-2 webhook system manages async generation results. Learn about request forwarding, notification handling, status reconciliation, and reservation settlement.

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

---

**The webhook system forwards generation requests to APIMart with a callback URL, receives asynchronous completion notifications at `/api/generation`, reconciles the task status against the database, and settles reservations via Supabase stored procedures.**

The Awesome GPT‑Image‑2 repository implements a robust **webhook system for async generation results** to handle compute‑intensive image creation from the APIMart service. Because the generation process runs asynchronously outside the HTTP request cycle, the server cannot return final images in the initial response. Instead, the architecture pushes task completion states to a dedicated callback endpoint, reconciles them against pending database reservations, and optionally exposes endpoints for client polling.

## Submitting the Generation Request with a Webhook URL

When a client initiates an image generation, the server in [`api/generate-image.js`](https://github.com/freestylefly/awesome-gpt-image-2/blob/main/api/generate-image.js) constructs an absolute webhook URL from the environment configuration and includes it in the payload sent to APIMart.

The system reads the `APP_URL` environment variable, strips trailing slashes, and appends `/api/generation` to form the callback address:

```javascript
const appUrl = String(process.env.APP_URL || '').replace(/\/$/, '');
const submitted = await submitApimartGeneration({
  apiKey: config.apiKey,
  prompt,
  language: body.language,
  webhook: appUrl ? `${appUrl}/api/generation` : ''   // ← callback endpoint
});

```

The `submitApimartGeneration` function (located in [`api/_lib/apimart.js`](https://github.com/freestylefly/awesome-gpt-image-2/blob/main/api/_lib/apimart.js)) transmits this URL to the APIMart API, which stores it and invokes a **POST** request upon task completion or failure.

## Receiving and Validating the Callback

The webhook listener resides in [`api/generation/callback.js`](https://github.com/freestylefly/awesome-gpt-image-2/blob/main/api/generation/callback.js). When APIMart calls the endpoint, the handler first validates the HTTP method and parses the JSON body using `readBody`.

It then extracts the task identifier via `extractApimartTaskId` and validates its format with `isValidApimartTaskId`:

```javascript
const taskId = extractApimartTaskId(payload);
if (!isValidApimartTaskId(taskId)) {
  // Return early for malformed identifiers
}

```

Only properly formatted task IDs proceed to the reconciliation phase.

## Reconciliation Logic and Database Settlement

The core processing occurs inside `reconcileApimartCallback`, an exported function that synchronizes external task states with internal database records. The logic follows four distinct steps:

**1. Locate the Pending Reservation**

The system queries the `generation_reservations` table via `findPlatformGeneration`, searching for a row where `provider_task_id` equals the incoming `taskId` and `status` equals `pending`.

**2. Handle Missing or Duplicate Callbacks**

If no reservation exists, the callback is ignored. If the reservation status is already settled, the request is treated as a duplicate:

```javascript
const reservation = await findReservation(client, taskId);
if (!reservation) return { state: 'ignored' };
if (reservation.status !== 'pending') return { state: 'duplicate' };

```

**3. Verify External Task Status**

The function fetches the latest task metadata from APIMart using `getApimartTask`. It proceeds only when the external status is `completed` or `failed`, waiting otherwise.

```javascript
const verifiedTask = await getApimartTask({ apiKey, taskId });
if (!['completed', 'failed'].includes(verifiedTask.status)) {
  return { state: 'pending' };
}

```

**4. Settle the Reservation**

Upon verification, `settlePlatformGeneration` updates the reservation row with final provider fields (cost, result URL, expiration) and invokes Supabase stored procedures:

- `complete_generation_reservation` for successful generations
- `release_generation_reservation` for failures, passing the appropriate error code

## Webhook Response Patterns

The endpoint returns specific HTTP status codes and JSON payloads to communicate processing outcomes to APIMart:

- **`202 Accepted`** with `{ ignored: true }` — No matching reservation found in the database.
- **`200 OK`** with `{ duplicate: true }` — The reservation was already settled; no action taken.
- **`202 Accepted`** with `{ pending: true }` — The external task remains in progress; APIMart should retry later.
- **`200 OK`** with `{ ok: true }` — The reservation has been successfully settled.

If reconciliation throws an unhandled exception, the endpoint returns **`503 Service Unavailable`** with the error code `CALLBACK_RECONCILIATION_FAILED`, signaling APIMart to retry the delivery.

## Optional Client-Side Polling

Although the webhook updates the database automatically, the front‑end can query real‑time status via the polling endpoint at **`GET /api/generation/status`** (implemented in [`api/generation/status.js`](https://github.com/freestylefly/awesome-gpt-image-2/blob/main/api/generation/status.js)).

This endpoint reads the reservation row and returns stored results if already settled. If the status remains pending, it queries APIMart on‑the‑fly via `getApimartTask` to return fresh state without waiting for the webhook push.

## Summary

- **Submission**: [`api/generate-image.js`](https://github.com/freestylefly/awesome-gpt-image-2/blob/main/api/generate-image.js) builds the webhook URL from `APP_URL` and registers it with APIMart via `submitApimartGeneration`.
- **Callback**: [`api/generation/callback.js`](https://github.com/freestylefly/awesome-gpt-image-2/blob/main/api/generation/callback.js) receives POST requests, validates task IDs, and delegates to `reconcileApimartCallback`.
- **Reconciliation**: The system matches callbacks to `generation_reservations` rows, verifies terminal states (`completed`/`failed`), and settles them via `complete_generation_reservation` or `release_generation_reservation`.
- **Responses**: Structured JSON responses inform APIMart whether to retry (202), acknowledge (200), or error (503).
- **Polling**: [`api/generation/status.js`](https://github.com/freestylefly/awesome-gpt-image-2/blob/main/api/generation/status.js) provides a fallback pull mechanism for client applications.

## Frequently Asked Questions

### What happens if the webhook callback fails or retries?

If the APIMart POST to `/api/generation` fails or returns a 503, APIMart will retry the delivery according to its own backoff policy. The reconciliation logic in `reconcileApimartCallback` is idempotent; duplicate callbacks for already‑settled reservations return `{ duplicate: true }` with HTTP 200, preventing double‑processing.

### How does the system prevent duplicate processing of the same generation task?

Before settling, `reconcileApimartCallback` checks the reservation status via `findPlatformGeneration`. If the row status is not `pending`, the function immediately returns `{ state: 'duplicate' }`. This ensures that only the first successful callback triggers the stored procedure, while subsequent retries are acknowledged but ignored.

### Can the webhook endpoint URL be customized to a different route?

The webhook URL is dynamically constructed in [`api/generate-image.js`](https://github.com/freestylefly/awesome-gpt-image-2/blob/main/api/generate-image.js) using the `APP_URL` environment variable hardcoded to the `/api/generation` path. To change the endpoint, you must modify the string interpolation `${appUrl}/api/generation` in the submission logic and create a corresponding handler file at the new path.

### What is the difference between the webhook callback and the status polling endpoint?

The **webhook callback** at `/api/generation` is a push‑mechanism invoked by APIMart when a task finishes; it updates the database proactively. The **status polling endpoint** at `/api/generation/status` is a pull‑mechanism that clients can query repeatedly; it reads from the database or queries APIMart directly if the result is still pending, enabling real‑time UI updates without WebSocket connections.