Mint a presigned S3 URL the client uploads bytes to
Stage 1 of the upload flow. Returns a single-use presigned PUT URL (5-minute TTL, locked to the specified contentType) the SPA / SDK uploads raw bytes to. Bypassing the Lambda avoids the 6 MB sync-invocation cap; the Lambda only sees the resulting image_key reference on stage 3 (POST /captures).
Auth: the verification id in the path :token IS the credential (same trust model as Stripe payment-intent client_secret). Any caller with the token can mint an upload URL — but the URL is single-use, time-limited, and locked to a specific content-type, so a stolen URL cannot be repurposed.
Capture id assignment: server-side, returned to the client. The same id flows back to the server on POST /captures via the image_key field (the key contains the id), keeping the dashboard’s display logic deterministic.
Authorizations
Possession of the verification id in the URL path (/v1/verifications/:token/...) authenticates the SPA capture flow. No header — the id is the credential. ULIDs are large enough that existence-by-id is not a meaningful leak. Used for POST /captures/upload-url, POST /captures, GET /v1/verify/:token.
Path Parameters
^vfy_[0-9A-HJKMNP-TV-Z]{26}$"vfy_01HXYZABCDEFGHJKMNPQRSTVWX"
Body
Body for POST /v1/verifications/:token/captures/upload-url. The returned presigned PUT URL is single-use, 5-minute TTL, locked to the specified contentType — a stolen URL cannot be repurposed for a different mime.