Genace

Video generation API duplicate requests: prevent repeat submissions in your app

Video generation API duplicate requests usually originate in an application retry, a double-click, or a queue message delivered twice. In the current Genace implementation, the generation route does not read an Idempotency-Key header. Each accepted submission creates a new job identifier and deducts credits for that job. Adding an invented header to your request does not provide server-side deduplication.

Protect the user action in your own application before you call the generation endpoint. The goal is one authorized submission attempt per local request, with a clear path for reconciling an uncertain outcome.

Create a request ledger

The following PostgreSQL table is an application design example. It is not a Genace endpoint or a table you can query through the public API.

CREATE TABLE render_requests (
  request_id text PRIMARY KEY,
  owner_id text NOT NULL,
  request_hash text NOT NULL,
  status text NOT NULL CHECK (
    status IN ('reserved', 'submitting', 'accepted', 'ambiguous', 'rejected')
  ),
  remote_job_id text,
  updated_at timestamptz NOT NULL DEFAULT now()
);

INSERT INTO render_requests
  (request_id, owner_id, request_hash, status)
VALUES ('checkout-preview-42', 'customer-17', 'sha256-of-canonical-input', 'reserved')
ON CONFLICT (request_id) DO NOTHING;

Use a database uniqueness constraint, not just a disabled browser button. Two servers can receive the same action concurrently, and both need to agree on which one owns submission. Validate that a reused request identifier belongs to the same owner and has the same canonical request hash. Reusing an identifier with different inputs should be an application conflict.

Track uncertainty explicitly

After reservation, acquire a submission lease or perform a conditional transition to submitting. Only that owner may send the POST. On a 202 response, persist job_id and mark the record accepted. Subsequent duplicate actions return the saved local status and identifier.

If the connection fails after sending the request, mark the attempt ambiguous. The server may already have deducted credits and submitted upstream. A lease expiring is evidence that your worker stopped; it is not proof that the remote request never happened. Avoid letting another worker automatically resubmit an ambiguous attempt.

ObservationLedger outcomeNext action
Saved 202 responseacceptedCollect the stored job
Explicit validation rejectionrejectedCorrect inputs before a new attempt
Socket closes after sendambiguousReconcile before resubmitting
Duplicate local actionExisting ledger rowReturn the existing outcome

Keep intentional variations possible

Do not deduplicate globally by prompt text alone. A user may intentionally request two different samples from the same prompt. Use an action identifier for the business request and keep its input hash as a consistency check. A deliberate variation receives a new action identifier and a separate budget decision.

Verification checklist

Replay the same queue message twice and confirm that only one worker obtains submission ownership. Test a repeated action with changed input, a crash after sending, and a duplicate arriving after acceptance. Verify that ambiguous records remain visible to operators instead of disappearing into an automatic retry loop.

See timeouts, retry backoff, and batch queues.

Continue with production operations

Where these facts come from

  • codebase: src/app/api/v1/video/generations/route.ts
  • codebase: src/ai/jobs/service.ts
  • Editorial keyword hypothesis; search volume not measured