DEV Community

Jesus Lopez
Jesus Lopez

Posted on

Handling retries and credits in an asynchronous AI video generator

An AI video generator can look simple from the outside: upload a photo, press a button, and wait for a video. The harder part is handling everything between those steps—duplicate clicks, slow provider responses, failed jobs, and credits that have already been charged.

I encountered these problems while building Outstanding Tiny Desk AI, a tool that turns a lead singer photo and an optional sax player photo into a short parody video. The video examples show the intended result; here are the decisions behind the generation flow.

Validate before starting an expensive job

The upload endpoint checks the actual image bytes, file type, size, and dimensions before sending a photo to the video provider. It also requires the user to confirm they have permission to use the photos.

This catches unusable input before a paid job starts. Checking the filename or browser-supplied MIME type alone would not be enough. The step-by-step guide also explains what makes a good input photo.

Treat retries as normal

A user might double-click, retry after a slow response, or open the same request in another tab. Charging for each request would be a bad outcome.

The generation endpoint uses an idempotency key tied to the user. It also fingerprints the photos, options, and template version. If the same input already has a running job, the endpoint returns that job instead of starting and charging for another one. Reusing a key with different input is rejected.

Keep the task and credit movement together

A video costs 10 credits. Creating its task and reserving those credits need to act as one operation, even when two requests arrive at nearly the same time. The implementation uses an atomic database batch so concurrent requests cannot spend the same credits twice.

A failed job returns its credits, but a temporary network error is not necessarily a failed job. The provider may have accepted the request even if the response never reached our server. In that uncertain state, the task waits for a callback or reconciliation instead of immediately refunding and risking a free duplicate generation.

Show the state the user is actually in

The interface distinguishes submitting, queued, generating, and saving from success or failure. Users can leave the page and find completed videos in My videos. Finished files are served through an authenticated download route.

The product deliberately supports one fixed video format rather than a general-purpose prompt box. That keeps the user flow short, though it also means the shots and song are not customizable. The template breakdown explains the fixed shot sequence.

How do you handle a provider request when you cannot tell whether it was accepted? I'd be interested in other approaches to that failure state.

Top comments (0)