{"openapi":"3.1.0","info":{"title":"Identity Verification POC","description":"\nLocal API to verify a person's identity against their ID document (Peruvian DNI)\nwith face comparison + passive liveness, and to sign a document bound to the\nverified identity (PAdES).\n\n- **Custom, executable docs:** [/api-docs](/api-docs)\n- **Swagger UI:** [/docs](/docs) · **ReDoc:** [/redoc](/redoc) · **OpenAPI:** [/openapi.json](/openapi.json)\n\nSession state machine: `CREATED → VERIFYING → {VERIFIED|REJECTED|REVIEW}`, then\n`VERIFIED → SIGNED`. Images are never persisted (Law 29733); responses never\ninclude raw biometrics. 100% local — no LLM, no external services.\n","contact":{"name":"Caramel Point","email":"mauricio@caramelpoint.com"},"version":"0.2.0"},"paths":{"/health":{"get":{"tags":["Utilities"],"summary":"Health check + loaded models","description":"Confirm the app is alive and the models/services loaded.","operationId":"health_health_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/verify":{"post":{"tags":["Stateless"],"summary":"Passive liveness + 1:1 face match","description":"Passive liveness + 1:1 face match (stateless).\n\nStack: YuNet + SFace (OpenCV Zoo) for matching and Silent-Face for liveness.\nAll local, no LLM or external services.\n\nFlow: liveness on the selfie -> if fake, REJECTED without comparing;\nif real, detection + document-vs-selfie comparison.","operationId":"verify_verify_post","requestBody":{"content":{"multipart/form-data":{"schema":{"$ref":"#/components/schemas/Body_verify_verify_post"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/detect-orientation":{"post":{"tags":["Utilities"],"summary":"Suggest rotation to straighten an image","description":"Helper for the UI: return the rotation (0/90/180/270) that makes the face\ndetectable, so the client can auto-straighten the preview before sending.\nStateless; does not run liveness or matching.","operationId":"detect_orientation_detect_orientation_post","requestBody":{"content":{"multipart/form-data":{"schema":{"$ref":"#/components/schemas/Body_detect_orientation_detect_orientation_post"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/sessions":{"post":{"tags":["Sessions"],"summary":"Create a verification session","description":"Create a verification session.\n\nRequires explicit consent to biometric processing (Law 29733); without it,\nit is rejected with 409. No biometrics are processed yet: it only opens the\nsession.","operationId":"create_session_sessions_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateSessionRequest"}}},"required":true},"responses":{"201":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateSessionResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/sessions/{session_id}/verify":{"post":{"tags":["Sessions"],"summary":"Run verification on the session","description":"Run the verification pipeline (liveness + match) on the session.\n\nReuses the recognition stack (YuNet + SFace + Silent-Face) and combines the\nsignals with the RENIEC / document-authenticity mock ports in the decision\nengine. Outcome: VERIFIED / REJECTED / REVIEW. Images are not persisted\n(Law 29733).","operationId":"verify_session_sessions__session_id__verify_post","parameters":[{"name":"session_id","in":"path","required":true,"schema":{"type":"string","title":"Session Id"}}],"requestBody":{"required":true,"content":{"multipart/form-data":{"schema":{"$ref":"#/components/schemas/Body_verify_session_sessions__session_id__verify_post"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SessionResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/sessions/{session_id}":{"get":{"tags":["Sessions"],"summary":"Get session state and decision","description":"Return the session's state and decision (no images or raw biometrics).","operationId":"get_session_sessions__session_id__get","parameters":[{"name":"session_id","in":"path","required":true,"schema":{"type":"string","title":"Session Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SessionResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/sessions/{session_id}/sign":{"post":{"tags":["Sessions"],"summary":"Sign a document bound to the verified identity","description":"Sign a PDF, binding it to the session's verified identity.\n\nOnly allowed if the session is VERIFIED (verify ≠ bind). The PAdES signature\nembeds the `session_id` and the holder, and leaves an entry in the audit trail.","operationId":"sign_document_sessions__session_id__sign_post","parameters":[{"name":"session_id","in":"path","required":true,"schema":{"type":"string","title":"Session Id"}}],"requestBody":{"required":true,"content":{"multipart/form-data":{"schema":{"$ref":"#/components/schemas/Body_sign_document_sessions__session_id__sign_post"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SignatureResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/sessions/{session_id}/signed-document":{"get":{"tags":["Sessions"],"summary":"Download the signed PDF","description":"Download the session's signed PDF (application/pdf).","operationId":"download_signed_document_sessions__session_id__signed_document_get","parameters":[{"name":"session_id","in":"path","required":true,"schema":{"type":"string","title":"Session Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}}},"components":{"schemas":{"Body_detect_orientation_detect_orientation_post":{"properties":{"image":{"type":"string","contentMediaType":"application/octet-stream","title":"Image","description":"Image to orient"}},"type":"object","required":["image"],"title":"Body_detect_orientation_detect_orientation_post"},"Body_sign_document_sessions__session_id__sign_post":{"properties":{"document_pdf":{"type":"string","contentMediaType":"application/octet-stream","title":"Document Pdf","description":"PDF to sign"}},"type":"object","required":["document_pdf"],"title":"Body_sign_document_sessions__session_id__sign_post"},"Body_verify_session_sessions__session_id__verify_post":{"properties":{"document":{"type":"string","contentMediaType":"application/octet-stream","title":"Document","description":"Document image (DNI)"},"selfie":{"type":"string","contentMediaType":"application/octet-stream","title":"Selfie","description":"Selfie image"}},"type":"object","required":["document","selfie"],"title":"Body_verify_session_sessions__session_id__verify_post"},"Body_verify_verify_post":{"properties":{"document":{"type":"string","contentMediaType":"application/octet-stream","title":"Document","description":"Document image (DNI)"},"selfie":{"type":"string","contentMediaType":"application/octet-stream","title":"Selfie","description":"Selfie image"}},"type":"object","required":["document","selfie"],"title":"Body_verify_verify_post"},"CreateSessionRequest":{"properties":{"consent":{"type":"boolean","title":"Consent","description":"Must be true to process (Law 29733)."},"consent_version":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Consent Version"},"dni":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Dni"},"client_reference":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Client Reference"}},"type":"object","required":["consent"],"title":"CreateSessionRequest"},"CreateSessionResponse":{"properties":{"session_id":{"type":"string","title":"Session Id"},"state":{"$ref":"#/components/schemas/SessionState"},"created_at":{"type":"string","title":"Created At"}},"type":"object","required":["session_id","state","created_at"],"title":"CreateSessionResponse"},"Decision":{"properties":{"outcome":{"$ref":"#/components/schemas/Outcome"},"reasons":{"items":{"$ref":"#/components/schemas/Reason"},"type":"array","title":"Reasons"},"scores":{"$ref":"#/components/schemas/Scores"},"reniec":{"$ref":"#/components/schemas/ReniecInfo"},"match_threshold":{"type":"number","title":"Match Threshold"},"evaluated_at":{"type":"string","title":"Evaluated At"}},"type":"object","required":["outcome","match_threshold","evaluated_at"],"title":"Decision","description":"Result of the decision engine (PRD §6.3)."},"HTTPValidationError":{"properties":{"detail":{"items":{"$ref":"#/components/schemas/ValidationError"},"type":"array","title":"Detail"}},"type":"object","title":"HTTPValidationError"},"Outcome":{"type":"string","enum":["APPROVED","REJECTED","REVIEW"],"title":"Outcome","description":"Outcome produced by the decision engine for a verification."},"Reason":{"type":"string","enum":["DOC_AUTHENTIC","LIVENESS_PASS","FACE_MATCH","DATA_MATCH","LIVENESS_FAIL","FACE_NO_MATCH","FACE_GRAY_ZONE","DATA_PARTIAL","DNI_INVALID"],"title":"Reason"},"ReniecInfo":{"properties":{"dni_status":{"type":"string","title":"Dni Status","default":"VALID"},"data_match":{"type":"string","title":"Data Match","default":"FULL"}},"type":"object","title":"ReniecInfo"},"Scores":{"properties":{"document_authenticity":{"type":"number","title":"Document Authenticity","default":0.0},"liveness":{"type":"number","title":"Liveness","default":0.0},"face_match":{"type":"number","title":"Face Match","default":0.0}},"type":"object","title":"Scores"},"SessionResponse":{"properties":{"session_id":{"type":"string","title":"Session Id"},"state":{"$ref":"#/components/schemas/SessionState"},"decision":{"anyOf":[{"$ref":"#/components/schemas/Decision"},{"type":"null"}]},"signed_document_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Signed Document Id"},"audit_trail_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Audit Trail Id"},"created_at":{"type":"string","title":"Created At"},"updated_at":{"type":"string","title":"Updated At"}},"type":"object","required":["session_id","state","created_at","updated_at"],"title":"SessionResponse"},"SessionState":{"type":"string","enum":["CREATED","VERIFYING","VERIFIED","REJECTED","REVIEW","SIGNED"],"title":"SessionState","description":"States of a verification session's state machine."},"SignatureResponse":{"properties":{"session_id":{"type":"string","title":"Session Id"},"state":{"$ref":"#/components/schemas/SessionState"},"signed_document_id":{"type":"string","title":"Signed Document Id"},"provider":{"type":"string","title":"Provider"},"document_hash":{"type":"string","title":"Document Hash"},"signed_hash":{"type":"string","title":"Signed Hash"},"signed_at":{"type":"string","title":"Signed At"},"audit_trail_id":{"type":"string","title":"Audit Trail Id"}},"type":"object","required":["session_id","state","signed_document_id","provider","document_hash","signed_hash","signed_at","audit_trail_id"],"title":"SignatureResponse"},"ValidationError":{"properties":{"loc":{"items":{"anyOf":[{"type":"string"},{"type":"integer"}]},"type":"array","title":"Location"},"msg":{"type":"string","title":"Message"},"type":{"type":"string","title":"Error Type"},"input":{"title":"Input"},"ctx":{"type":"object","title":"Context"}},"type":"object","required":["loc","msg","type"],"title":"ValidationError"}}},"tags":[{"name":"Stateless","description":"One-shot liveness + 1:1 face match, no session."},{"name":"Sessions","description":"Stateful verify → sign flow with audit trail."},{"name":"Utilities","description":"Health and helper endpoints."}]}