Close a case for batch analysis
New in GET-59. Attaches the claimant’s written narrative to the verification and emits verification.case.completed on the internal EventBridge bus. The case-analysis worker (rial-platform-worker-staging only today) picks that event up, fetches every capture associated with the verification, and runs Gemini multi-image cross-reasoning over the batch + narrative. The resulting CaseAnalysisReport is logged to CloudWatch and (in a follow-up slice) persisted onto the verification.
Idempotency: atomic conditional write — narrative-not-already-set AND status in {pending, partially_captured}. Either guard failing returns 409 already_finalized. The handler does NOT compare narratives; a retry with the same narrative still 409s. Callers wanting idempotency-on-same-narrative can probe via GET /v1/verifications/:id first.
Publish discipline: if the bus emit fails, the DB write still succeeded so the request returns 200. Failing the request would force a retry which would then 409 against attribute_not_exists(narrative) and leave the operator stuck. Bus failure is logged for ops visibility.
Production status: worker is CASE_ANALYSIS=stub on prod per ADR-002 sibling deferral — the endpoint accepts the call and the event fires, but no real analysis runs until the first pay-customer onboards verify.get-rial.com.
Authorizations
Dashboard / operator session cookie. Set by /v1/auth/google/callback after Google OAuth, JWT signed with the rotating rial-platform/session-signing HMAC. Read on every request the SPA makes with credentials: include. Browser-only — server-to-server callers use bearerApiKey instead.
Path Parameters
ULID with vfy_ prefix.
^vfy_[0-9A-HJKMNP-TV-Z]{26}$"vfy_01HXYZABCDEFGHJKMNPQRSTVWX"
Body
Body for POST /v1/verifications/:id/finalize. narrative is the claimant's written description that the case-analysis worker cross-references against the uploaded captures.
1 - 2000Response
Case finalized. Response is the updated verification with narrative now present.
Canonical wire shape for a verification. narrative is present only after POST /v1/verifications/:id/finalize ran. verdict is present only after analysis completed. case_analysis is the structured Gemini multi-image rubric, embedded inline by the sync finalize path when analysis succeeded.
^vfy_[0-9A-HJKMNP-TV-Z]{26}$Lifecycle state. pending → first capture flips to partially_captured → analysis sets the verdict and transitions to completed. Terminal: completed, expired, failed. abandoned is a side-branch off pending/partially_captured, reported by the capture screen via POST /v1/verifications/:token/progress when the end user leaves before finishing — NOT terminal: a later capture resurrects the row to partially_captured like any other.
pending, partially_captured, completed, expired, failed, abandoned x >= 0Tenant-supplied configuration baked into the token at issue time. Immutable after creation — changing config means issuing a new verification. steps is the normalized capture spec: requests that sent max_captures: N read back as a single anonymous step { key: "capture_1", min: N, max: N }.
Integrity seal over the verification record including answers. Present only when answers exist and sealing is on (seal !== false). The hash proves the stored answers have not changed — it does NOT claim they are true or sensor-attested; answers_provenance carries that distinction explicitly.
Aggregate verdict produced by the per-capture analysis pipeline. label is the human-readable bucket; score is the confidence in the verdict.
2000Structured output from the multi-image case-analysis worker (Gemini batch reasoning). Per-dimension scores 0..1 where 0 = nothing suspicious on that dimension and 1 = that dimension alone is grounds to reject. overall_risk_score aggregates with cross-dimensional reasoning, not a simple average.
Object-check result. Present only when the verification was created with expected_object. Independent of the fraud verdict.
Condition assessment. Present only when the verification was created with condition_aspects — the free-form tenant-defined aspect names (any language, any domain). score is the one-decimal average of aspect scores; label buckets it (>=7.5 good, >=5 fair, else poor). Independent of the fraud verdict.
Latest funnel-progress snapshot reported via POST /v1/verifications/:token/progress. A snapshot, not an event log — each call overwrites the previous one. Operator-only; never present on the public projection.