Skip to content

fix(web): Infra upload idempotency, repeat transcript delete and no-store developer responses - #4176

Merged
richiemcilroy merged 11 commits into
mainfrom
fix/infra-rest-polish
Nov 13, 2026
Merged

richiemcilroy merged 11 commits into
mainfrom
fix/infra-rest-polish

Conversation

@richiemcilroy

@richiemcilroy richiemcilroy commented Nov 13, 2026 •

Copy link
Copy Markdown
Member

Four small fixes to the Cap Infra developer REST API.

Idempotency-Key on create upload

POST /api/developer/v1/uploads now accepts an Idempotency-Key header with the same rules as the transcript routes: 8 to 128 characters from letters, digits, dot, dash and underscore. A key that breaks those rules gets 400 invalid_idempotency_key.

  • The video id comes from a hash of the app id and the key, so racing duplicates collide on the primary key. The insert uses on duplicate key update, then reads the row back. No table or column was added.
  • A repeat with the same name, userId, maxResolutionTier and metadata returns the same video and playback id with a fresh upload token. Metadata is compared after sorting its keys.
  • A repeat with a different body gets 409 idempotency_conflict, and the message names the field that changed.
  • Two more cases get 409. A key whose video was deleted gets idempotency_conflict. A repeat after the upload completed gets upload_already_completed, the same answer the token route gives.
  • Upload admission runs first on every request, repeats included. Creating a video does not charge, so a repeat cannot charge twice.
  • The key validation moves to lib/developer-ids.ts and the transcript routes share it.

The agent API's create upload already derives its id from a key in createDeveloperVideoUpload and does not share this code, so it is unchanged.

Repeat delete of a transcript

DELETE /api/developer/v1/transcripts/{id} on a transcript this app already deleted now answers 200 { "success": true }. It runs the file cleanup again, which includes the captions sync from #4134. An id that never existed, or another app's transcript, still answers 404.

Cache-Control and X-Request-Id

A middleware registered first on both Hono apps (/api/developer/v1 and /api/developer/sdk/v1) sets Cache-Control: private, no-store on every response. That covers errors, 404s from notFound and 429s from the rate limiter. When Vercel sends x-vercel-id, the middleware echoes it as X-Request-Id, and the SDK CORS config adds that header to Access-Control-Expose-Headers.

The public OpenAPI and docs routes are untouched. The agent API (/api/v1) is a single Effect HttpApi, so scoping the header to its developer routes is more than a one-line change. It is left for a follow-up.

Docs

rest-api.mdx and the OpenAPI spec cover:

  • the create upload key, plus its 409 response
  • the repeat delete
  • the request id header

Tests

  • New infra-upload-idempotency.test.ts runs against an in-memory developer_videos behind drizzle's mysql proxy. It covers:
    • a duplicate gives one video
    • five racing requests give one video
    • a conflict on each of the four fields
    • a deleted video and a completed upload
    • admission on a repeat
    • per-app separation, and no key
  • developer-api-contract.test.ts checks:
    • the key reaches createInfraVideo
    • four bad keys give 400
    • private, no-store on v1 200, 401, 404, 405 and 429 answers, and on SDK 200 and 401 answers
    • the request id is echoed and exposed
  • developer-transcript-store.test.ts and developer-video-captions.test.ts cover the repeat delete and the 404 cases.

Web unit suite: 125 files, 2,556 passed. The transcripts database suite passed 39 tests against a scratch MySQL. Against main's source files, 20 of the new or changed tests fail.

The headers were tested through Hono in process, not on a Vercel deployment. Check them on the preview.

RetriggerView in GreptileConfidence Score: 5/5

The PR appears safe to merge; no new actionable issues were found.

Summary

Adds safe upload retries, repeat transcript deletion, and private response headers to the developer APIs.

  • Upload retries with the same key return the same video.
  • Deleting a transcript again returns success for its app.
  • Developer REST and SDK responses carry private cache and request ID headers.

No new actionable issues were found. Tests and deployed headers were not independently run or checked.

Reviews (4) · Last reviewed commit: "Merge branch 'main' into fix/infra-rest-..." · Reviewed by Greptile

@capy-ai capy-ai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Capy found no issues.

View 1 other finding in Capy.

Open in Capy

Comment thread apps/web/lib/developer-openapi.ts Resolved
@vercel

vercel Bot commented Nov 13, 2026 •

Copy link
Copy Markdown
Contributor

The latest updates on your projects. Learn more about Vercel for GitHub.

1 Skipped Deployment
Project Deployment Actions Updated
cap-status Skipped Skipped Nov 13, 2026 6:16am UTC

@vercel
vercel Bot temporarily deployed to Preview – cap-status November 13, 2026 05:58 Inactive
@richiemcilroy

Copy link
Copy Markdown
Member Author

hey @greptileai, please re-review the PR

@capy-ai capy-ai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Capy found no issues.

Open in Capy

@sandchest-com

sandchest-com Bot commented Nov 13, 2026 •

Copy link
Copy Markdown

Sandchest

Sandchest reviewed 7c554fd: nothing exploitable found in this change.

Sandchest reviews every change for vulnerabilities an attacker could use · 12 credits · Review details

@vercel
vercel Bot temporarily deployed to Preview – cap-status November 13, 2026 06:01 Inactive

@capy-ai capy-ai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Capy found no issues.

Open in Capy

@vercel
vercel Bot temporarily deployed to Preview – cap-status November 13, 2026 06:03 Inactive

@capy-ai capy-ai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Capy found no issues.

Open in Capy

@vercel
vercel Bot temporarily deployed to Preview – cap-status November 13, 2026 06:09 Inactive
@richiemcilroy

Copy link
Copy Markdown
Member Author

hey @greptileai, please re-review the PR

@capy-ai capy-ai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Capy found no issues.

View 1 other finding in Capy.

Open in Capy

@richiemcilroy

Copy link
Copy Markdown
Member Author

hey @greptileai, please re-review the PR

@vercel
vercel Bot temporarily deployed to Preview – cap-status November 13, 2026 06:12 Inactive

@capy-ai capy-ai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Capy found no issues.

Open in Capy

@vercel
vercel Bot temporarily deployed to Preview – cap-status November 13, 2026 06:16 Inactive

@capy-ai capy-ai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Capy found no issues.

Open in Capy

@richiemcilroy
richiemcilroy merged commit fc4092e into main Nov 13, 2026
38 checks passed

This branch was successfully deployed

1 active and 1 inactive deployments
Preview – cap-web — 7c554fdf Deployed Nov 13, 2026 by vercel[bot]
Preview – cap-status — 7c554fdf Deployed Nov 13, 2026 by vercel[bot]
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant