{"openapi":"3.0.3","info":{"title":"KYC Verification Service API","description":"Auth, health, and system APIs. Session cookie `kyc_session` is set on successful login; use `credentials: \"include\"` for `fetch`.","version":"0.1.0","license":{"name":"Proprietary"}},"servers":[{"url":"http://localhost:4488","description":"Local development"}],"security":[],"tags":[{"name":"Auth","description":"Authentication"},{"name":"Organizations","description":"Tenant organizations"},{"name":"Verification","description":"Identity verification sessions (opaque token via `X-Verification-Token` or `?token=`)"},{"name":"System","description":"Health and metadata"},{"name":"Dashboard","description":"Operator/compliance views (session cookie `kyc_session`; same as auth login)"}],"components":{"schemas":{"ApiError":{"type":"object","required":["ok","error"],"properties":{"ok":{"type":"boolean","enum":[false]},"error":{"type":"string","description":"Machine-readable code","example":"VALIDATION_ERROR"},"message":{"type":"string"},"fieldErrors":{"type":"object","additionalProperties":{"type":"string"}}},"description":"Generic API error envelope. Prefer operation-specific schemas that narrow `error`."},"Login400Error":{"allOf":[{"$ref":"#/components/schemas/ApiError"},{"type":"object","required":["error"],"properties":{"error":{"type":"string","enum":["INVALID_JSON","VALIDATION_ERROR"],"description":"Machine-readable code for this HTTP status on this operation"}}}]},"Login401Error":{"allOf":[{"$ref":"#/components/schemas/ApiError"},{"type":"object","required":["error"],"properties":{"error":{"type":"string","enum":["INVALID_CREDENTIALS"],"description":"Machine-readable code for this HTTP status on this operation"}}}]},"Login503Error":{"allOf":[{"$ref":"#/components/schemas/ApiError"},{"type":"object","required":["error"],"properties":{"error":{"type":"string","enum":["SERVICE_UNAVAILABLE","SESSION_NOT_CONFIGURED"],"description":"Machine-readable code for this HTTP status on this operation"}}}]},"Signup400Error":{"allOf":[{"$ref":"#/components/schemas/ApiError"},{"type":"object","required":["error"],"properties":{"error":{"type":"string","enum":["INVALID_JSON","VALIDATION_ERROR"],"description":"Machine-readable code for this HTTP status on this operation"}}}]},"Signup409Error":{"allOf":[{"$ref":"#/components/schemas/ApiError"},{"type":"object","required":["error"],"properties":{"error":{"type":"string","enum":["EMAIL_ALREADY_REGISTERED"],"description":"Machine-readable code for this HTTP status on this operation"}}}]},"Signup500Error":{"allOf":[{"$ref":"#/components/schemas/ApiError"},{"type":"object","required":["error"],"properties":{"error":{"type":"string","enum":["CREATE_FAILED","INTERNAL_ERROR"],"description":"Machine-readable code for this HTTP status on this operation"}}}]},"Signup503Error":{"allOf":[{"$ref":"#/components/schemas/ApiError"},{"type":"object","required":["error"],"properties":{"error":{"type":"string","enum":["SERVICE_UNAVAILABLE","DATABASE_SCHEMA_MISSING"],"description":"Machine-readable code for this HTTP status on this operation"}}}]},"Health500Error":{"allOf":[{"$ref":"#/components/schemas/ApiError"},{"type":"object","required":["error"],"properties":{"error":{"type":"string","enum":["INTERNAL_ERROR"],"description":"Machine-readable code for this HTTP status on this operation"}}}]},"OrganizationsGet401Error":{"allOf":[{"$ref":"#/components/schemas/ApiError"},{"type":"object","required":["error"],"properties":{"error":{"type":"string","enum":["UNAUTHENTICATED"],"description":"Machine-readable code for this HTTP status on this operation"}}}]},"OrganizationsGet503Error":{"allOf":[{"$ref":"#/components/schemas/ApiError"},{"type":"object","required":["error"],"properties":{"error":{"type":"string","enum":["SERVICE_UNAVAILABLE","DATABASE_SCHEMA_MISSING"],"description":"Machine-readable code for this HTTP status on this operation"}}}]},"OrganizationsGet500Error":{"allOf":[{"$ref":"#/components/schemas/ApiError"},{"type":"object","required":["error"],"properties":{"error":{"type":"string","enum":["INTERNAL_ERROR"],"description":"Machine-readable code for this HTTP status on this operation"}}}]},"OrganizationsPost400Error":{"allOf":[{"$ref":"#/components/schemas/ApiError"},{"type":"object","required":["error"],"properties":{"error":{"type":"string","enum":["INVALID_JSON","VALIDATION_ERROR"],"description":"Machine-readable code for this HTTP status on this operation"}}}]},"OrganizationsPost401Error":{"allOf":[{"$ref":"#/components/schemas/ApiError"},{"type":"object","required":["error"],"properties":{"error":{"type":"string","enum":["UNAUTHENTICATED"],"description":"Machine-readable code for this HTTP status on this operation"}}}]},"OrganizationsPost409Error":{"allOf":[{"$ref":"#/components/schemas/ApiError"},{"type":"object","required":["error"],"properties":{"error":{"type":"string","enum":["CUSTOMER_ID_TAKEN"],"description":"Machine-readable code for this HTTP status on this operation"}}}]},"OrganizationsPost500Error":{"allOf":[{"$ref":"#/components/schemas/ApiError"},{"type":"object","required":["error"],"properties":{"error":{"type":"string","enum":["CREATE_FAILED","INTERNAL_ERROR"],"description":"Machine-readable code for this HTTP status on this operation"}}}]},"OrganizationsPost503Error":{"allOf":[{"$ref":"#/components/schemas/ApiError"},{"type":"object","required":["error"],"properties":{"error":{"type":"string","enum":["SERVICE_UNAVAILABLE","DATABASE_SCHEMA_MISSING"],"description":"Machine-readable code for this HTTP status on this operation"}}}]},"OrganizationSummary":{"type":"object","required":["id","customerId","displayName","subtitle"],"properties":{"id":{"type":"string","format":"uuid"},"customerId":{"type":"string"},"displayName":{"type":"string"},"subtitle":{"type":"string"}}},"OrganizationsListOk":{"type":"object","required":["ok","organizations"],"properties":{"ok":{"type":"boolean","enum":[true]},"organizations":{"type":"array","items":{"$ref":"#/components/schemas/OrganizationSummary"}}}},"CreateOrganizationRequest":{"type":"object","required":["customerId","legalEmail","displayName"],"properties":{"customerId":{"type":"string","minLength":1,"maxLength":255},"legalCompanyName":{"type":"string","maxLength":500},"legalEmail":{"type":"string","format":"email"},"registrationNumber":{"type":"string","maxLength":120},"companyWebsite":{"type":"string","maxLength":2048},"companyAddress":{"type":"string","maxLength":4000},"displayName":{"type":"string","minLength":1,"maxLength":200},"aliasName":{"type":"string","maxLength":200}}},"OrganizationPublic":{"type":"object","required":["id","customerId","legalEmail","displayName","createdAt","updatedAt"],"properties":{"id":{"type":"string","format":"uuid"},"customerId":{"type":"string"},"legalCompanyName":{"type":"string","nullable":true},"legalEmail":{"type":"string","format":"email"},"registrationNumber":{"type":"string","nullable":true},"companyWebsite":{"type":"string","nullable":true},"companyAddress":{"type":"string","nullable":true},"displayName":{"type":"string"},"aliasName":{"type":"string","nullable":true},"logoUrl":{"type":"string","nullable":true},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"}}},"CreateOrganizationOk":{"type":"object","required":["ok","organization"],"properties":{"ok":{"type":"boolean","enum":[true]},"organization":{"$ref":"#/components/schemas/OrganizationPublic"}}},"UserPublic":{"type":"object","required":["id","email","fullName"],"properties":{"id":{"type":"string","format":"uuid"},"email":{"type":"string","format":"email"},"fullName":{"type":"string"}}},"LoginRequest":{"type":"object","required":["email","password"],"properties":{"email":{"type":"string","format":"email"},"password":{"type":"string","minLength":1,"maxLength":128},"rememberDevice":{"type":"boolean","default":false}}},"LoginOk":{"type":"object","required":["ok","user"],"properties":{"ok":{"type":"boolean","enum":[true]},"user":{"$ref":"#/components/schemas/UserPublic"}}},"SignupRequest":{"type":"object","required":["fullName","email","password","acceptedTerms"],"properties":{"fullName":{"type":"string","minLength":1,"maxLength":120},"email":{"type":"string","format":"email"},"password":{"type":"string","minLength":8,"maxLength":128},"acceptedTerms":{"type":"boolean","enum":[true]}}},"SignupOk":{"type":"object","required":["ok","user"],"properties":{"ok":{"type":"boolean","enum":[true]},"user":{"$ref":"#/components/schemas/UserPublic"}}},"LogoutOk":{"type":"object","required":["ok"],"properties":{"ok":{"type":"boolean","enum":[true]}}},"HealthCheck":{"type":"object","required":["status","code"],"properties":{"status":{"type":"string","enum":["healthy","unhealthy","skipped"]},"code":{"type":"string","enum":["HEALTH_CHECK_API","HEALTH_CHECK_DB_HEALTHY","HEALTH_CHECK_DB_SKIPPED","HEALTH_CHECK_DB_UNHEALTHY"]},"latencyMs":{"type":"number"},"message":{"type":"string"}}},"HealthResponse":{"type":"object","required":["status","service","version","timestamp","responseTimeMs","checks","runtime"],"properties":{"status":{"type":"string","enum":["healthy","degraded","unhealthy"]},"service":{"type":"string","enum":["kyc-verification-service"]},"version":{"type":"string"},"timestamp":{"type":"string","format":"date-time"},"responseTimeMs":{"type":"number"},"checks":{"type":"object","required":["api","database"],"properties":{"api":{"$ref":"#/components/schemas/HealthCheck"},"database":{"$ref":"#/components/schemas/HealthCheck"}}},"runtime":{"type":"object","required":["node","env"],"properties":{"node":{"type":"string"},"env":{"type":"string"}}}}},"VerificationDocumentAsset":{"type":"object","required":["id","side","analysisStatus","s3Key","createdAt"],"properties":{"id":{"type":"string","format":"uuid"},"side":{"type":"string","enum":["front","back","single","poa"]},"analysisStatus":{"type":"string"},"s3Key":{"type":"string"},"createdAt":{"type":"string","format":"date-time"}}},"VerificationSessionPublic":{"type":"object","required":["id","documentType","capturePhase","reviewStatus","riskLabel","automatedBlockReason","expiresAt","assets"],"properties":{"id":{"type":"string","format":"uuid"},"documentType":{"type":"string","nullable":true,"enum":["passport","id_card","drive_eta"]},"capturePhase":{"type":"string"},"reviewStatus":{"type":"string"},"riskLabel":{"type":"string","nullable":true},"automatedBlockReason":{"type":"string","nullable":true},"expiresAt":{"type":"string","format":"date-time"},"assets":{"type":"array","items":{"$ref":"#/components/schemas/VerificationDocumentAsset"}}}},"CreateVerificationSessionRequest":{"type":"object","description":"Optional JSON body when `Content-Type` is `application/json`. All fields are optional.","properties":{"externalUserId":{"type":"string","minLength":1,"maxLength":512,"description":"End-user id in the integrating platform (tenant)"},"returnUrl":{"type":"string","format":"uri","maxLength":2048,"description":"End-user redirect target after verification completes. `?session_id=…&status=…` is appended."},"webhookUrl":{"type":"string","format":"uri","maxLength":2048,"description":"Per-session webhook URL for `verification.completed` and `verification.extraction` events."},"webhookSecret":{"type":"string","minLength":16,"maxLength":255,"description":"HMAC-SHA256 signing secret for this session's webhooks; requires `webhookUrl`."},"metadata":{"type":"object","additionalProperties":true,"description":"Opaque tenant metadata echoed back in every webhook payload for this session."}}},"CreateVerificationSessionOk":{"type":"object","required":["ok","sessionId","token","clientSecret","hostedUrl","expiresAt"],"properties":{"ok":{"type":"boolean","enum":[true]},"sessionId":{"type":"string","format":"uuid"},"token":{"type":"string","description":"Opaque per-session token. Same value as `clientSecret`; kept for legacy clients."},"clientSecret":{"type":"string","description":"Opaque per-session token. Pass to the SDK `Uniicy.mount({ clientSecret })`."},"hostedUrl":{"type":"string","format":"uri","description":"Absolute URL of the hosted verification flow. Tenants can `302 →` this."},"expiresAt":{"type":"string","format":"date-time"}}},"DashboardSessionSummary":{"type":"object","required":["id","externalUserId","documentType","capturePhase","reviewStatus","riskLabel","automatedBlockReason","documentHeatmapViewUrl","faceSimilarityScore","expiresAt","createdAt","updatedAt"],"properties":{"id":{"type":"string","format":"uuid"},"externalUserId":{"type":"string","nullable":true},"documentType":{"type":"string","nullable":true},"capturePhase":{"type":"string"},"reviewStatus":{"type":"string"},"riskLabel":{"type":"string","nullable":true},"automatedBlockReason":{"type":"string","nullable":true},"documentHeatmapViewUrl":{"type":"string","nullable":true,"description":"Short-lived signed GET for document moiré heatmap when stored; null on list responses."},"elaHeatmapViewUrl":{"type":"string","nullable":true,"description":"Short-lived signed GET for ELA tampering heatmap when stored."},"faceSimilarityScore":{"type":"number","nullable":true},"expiresAt":{"type":"string","format":"date-time"},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"}}},"DashboardLatestLiveness":{"type":"object","required":["id","status","confidenceScore","failureReason","riskLabel","signalsJson","updatedAt"],"properties":{"id":{"type":"string","format":"uuid"},"status":{"type":"string"},"confidenceScore":{"type":"number","nullable":true},"failureReason":{"type":"string","nullable":true},"riskLabel":{"type":"string","nullable":true},"signalsJson":{"type":"object","additionalProperties":true,"nullable":true},"updatedAt":{"type":"string","format":"date-time"}}},"DashboardVerificationsListOk":{"type":"object","required":["ok","items"],"properties":{"ok":{"type":"boolean","enum":[true]},"items":{"type":"array","items":{"type":"object","required":["session","latestLiveness"],"properties":{"session":{"$ref":"#/components/schemas/DashboardSessionSummary"},"latestLiveness":{"type":"object","nullable":true,"allOf":[{"$ref":"#/components/schemas/DashboardLatestLiveness"}]}}}}}},"DashboardVerificationAsset":{"type":"object","required":["id","side","analysisStatus","s3Key","contentType","createdAt","documentViewUrl"],"properties":{"id":{"type":"string","format":"uuid"},"side":{"type":"string"},"analysisStatus":{"type":"string"},"s3Key":{"type":"string"},"contentType":{"type":"string"},"createdAt":{"type":"string","format":"date-time"},"documentViewUrl":{"type":"string","nullable":true}}},"DashboardLivenessAttempt":{"type":"object","required":["id","clientRequestId","status","confidenceScore","failureReason","riskLabel","videoReferenceUrl","livenessVideoPlaybackUrl","signalsJson","createdAt","updatedAt"],"properties":{"id":{"type":"string","format":"uuid"},"clientRequestId":{"type":"string","format":"uuid"},"status":{"type":"string"},"confidenceScore":{"type":"number","nullable":true},"failureReason":{"type":"string","nullable":true},"riskLabel":{"type":"string","nullable":true},"videoReferenceUrl":{"type":"string","nullable":true,"description":"MinIO object key when a recording was stored"},"livenessVideoPlaybackUrl":{"type":"string","nullable":true,"description":"Short-lived signed GET URL when storage is configured"},"signalsJson":{"type":"object","additionalProperties":true,"nullable":true},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"}}},"DashboardAuditEvent":{"type":"object","required":["id","eventType","actorType","actorUserId","payload","rationale","createdAt"],"properties":{"id":{"type":"string","format":"uuid"},"eventType":{"type":"string"},"actorType":{"type":"string"},"actorUserId":{"type":"string","format":"uuid","nullable":true},"payload":{"type":"object","additionalProperties":true,"nullable":true},"rationale":{"type":"string","nullable":true},"modelVersions":{"type":"object","additionalProperties":true,"nullable":true,"description":"Model IDs keyed by service name (documentApi, inferenceApi, gemma)."},"rawScores":{"type":"object","additionalProperties":true,"nullable":true,"description":"Raw inference scores keyed by metric name (elaScore, fftScore, forensicRiskScore)."},"createdAt":{"type":"string","format":"date-time"}}},"DashboardAnalysisResultRow":{"type":"object","required":["assetId","extracted","modelId","createdAt"],"properties":{"assetId":{"type":"string","format":"uuid"},"extracted":{"type":"object","additionalProperties":true},"modelId":{"type":"string","nullable":true},"createdAt":{"type":"string","format":"date-time"}}},"DashboardVerificationDetailOk":{"type":"object","required":["ok","session","assets","analysisResults","livenessAttempts","auditEvents"],"properties":{"ok":{"type":"boolean","enum":[true]},"session":{"$ref":"#/components/schemas/DashboardSessionSummary"},"assets":{"type":"array","items":{"$ref":"#/components/schemas/DashboardVerificationAsset"}},"analysisResults":{"type":"array","items":{"$ref":"#/components/schemas/DashboardAnalysisResultRow"}},"livenessAttempts":{"type":"array","items":{"$ref":"#/components/schemas/DashboardLivenessAttempt"}},"auditEvents":{"type":"array","items":{"$ref":"#/components/schemas/DashboardAuditEvent"}}}},"DashboardVerificationReviewRequest":{"type":"object","required":["decision","rationale"],"properties":{"decision":{"type":"string","enum":["approve","reject","request_more_info"]},"rationale":{"type":"string","minLength":10,"maxLength":4000},"manualMrzLine1":{"type":"string","minLength":20,"maxLength":120},"manualMrzLine2":{"type":"string","minLength":20,"maxLength":120},"overrideReason":{"type":"string","enum":["ocr_skew_or_glare","image_quality_recoverable","system_false_positive","operator_verified_physically","other"]}}},"DashboardVerificationReviewOk":{"type":"object","required":["ok","reviewStatus"],"properties":{"ok":{"type":"boolean","enum":[true]},"reviewStatus":{"type":"string"}}},"DashboardVerificationAssetReanalyzeOk":{"type":"object","required":["ok","assetId"],"properties":{"ok":{"type":"boolean","enum":[true]},"assetId":{"type":"string","format":"uuid"}}},"RetrainingNotifyRequest":{"type":"object","required":["sessionIds"],"properties":{"sessionIds":{"type":"array","minItems":1,"maxItems":500,"items":{"type":"string","format":"uuid"}},"note":{"type":"string","maxLength":2000}}},"RetrainingNotifyOk":{"type":"object","required":["ok","recorded","sessionCount"],"properties":{"ok":{"type":"boolean","enum":[true]},"recorded":{"type":"boolean"},"sessionCount":{"type":"integer"}}},"GetVerificationSessionOk":{"type":"object","required":["ok","session"],"properties":{"ok":{"type":"boolean","enum":[true]},"session":{"$ref":"#/components/schemas/VerificationSessionPublic"}}},"PatchVerificationSessionRequest":{"type":"object","required":["documentType","token"],"properties":{"documentType":{"type":"string","enum":["passport","id_card","drive_eta"]},"token":{"type":"string","minLength":1}}},"PresignVerificationUploadRequest":{"type":"object","required":["token","side","contentType","byteSize"],"properties":{"token":{"type":"string","minLength":1},"side":{"type":"string","enum":["front","back","single","poa"]},"contentType":{"type":"string","enum":["image/jpeg","image/png","image/webp","application/pdf"]},"byteSize":{"type":"integer","minimum":1,"maximum":10485760}}},"PresignVerificationUploadOk":{"type":"object","required":["ok","uploadUrl","s3Key","expiresInSeconds"],"properties":{"ok":{"type":"boolean","enum":[true]},"uploadUrl":{"type":"string"},"s3Key":{"type":"string"},"expiresInSeconds":{"type":"integer"}}},"CompleteVerificationUploadRequest":{"type":"object","required":["token","side","s3Key","contentType","byteSize","sha256"],"properties":{"token":{"type":"string","minLength":1},"side":{"type":"string","enum":["front","back","single","poa"]},"s3Key":{"type":"string","minLength":1},"contentType":{"type":"string","enum":["image/jpeg","image/png","image/webp","application/pdf"]},"byteSize":{"type":"integer","minimum":1,"maximum":10485760},"sha256":{"type":"string","pattern":"^[a-fA-F0-9]{64}$"}}},"CompleteVerificationUploadOk":{"type":"object","required":["ok","session","assetId"],"properties":{"ok":{"type":"boolean","enum":[true]},"session":{"$ref":"#/components/schemas/VerificationSessionPublic"},"assetId":{"type":"string","format":"uuid"}}},"LivenessLandmarksSummary":{"type":"object","required":["yawMin","yawMax","pitchMin","pitchMax","faceMotionVariance","depthVarianceProxy"],"properties":{"yawMin":{"type":"number"},"yawMax":{"type":"number"},"pitchMin":{"type":"number"},"pitchMax":{"type":"number"},"faceMotionVariance":{"type":"number"},"depthVarianceProxy":{"type":"number"}}},"LivenessInitOk":{"type":"object","required":["ok","requestId"],"properties":{"ok":{"type":"boolean","enum":[true]},"requestId":{"type":"string","format":"uuid"}}},"LivenessCompleteRequest":{"type":"object","required":["token","requestId","challengeType","greenChannelMean","landmarksSummary"],"properties":{"token":{"type":"string","minLength":1},"requestId":{"type":"string","format":"uuid"},"challengeType":{"type":"string","enum":["blink","head_turn"]},"greenChannelMean":{"type":"array","minItems":30,"maxItems":200,"items":{"type":"number"}},"landmarksSummary":{"$ref":"#/components/schemas/LivenessLandmarksSummary"},"texturePatchB64":{"type":"string","minLength":1,"nullable":true},"videoS3Key":{"type":"string","minLength":1,"maxLength":512,"description":"MinIO object key after PUT to presign-video (optional recording)"},"videoByteSize":{"type":"integer","minimum":1,"maximum":26214400,"description":"Required with videoS3Key; must match HEAD Content-Length"},"videoSha256":{"type":"string","pattern":"^[a-fA-F0-9]{64}$","description":"Optional integrity digest of the uploaded blob"}}},"LivenessPresignVideoRequest":{"type":"object","required":["token","requestId","contentType","byteSize"],"properties":{"token":{"type":"string","minLength":1},"requestId":{"type":"string","format":"uuid"},"contentType":{"type":"string","description":"e.g. video/webm or video/webm;codecs=vp8"},"byteSize":{"type":"integer","minimum":1,"maximum":26214400}}},"LivenessPresignVideoOk":{"type":"object","required":["ok","uploadUrl","s3Key","expiresInSeconds"],"properties":{"ok":{"type":"boolean","enum":[true]},"uploadUrl":{"type":"string","minLength":1},"s3Key":{"type":"string","minLength":1},"expiresInSeconds":{"type":"integer"}}},"LivenessCompleteOk":{"type":"object","required":["ok","passed","confidenceScore","failureCodes"],"properties":{"ok":{"type":"boolean","enum":[true]},"passed":{"type":"boolean"},"confidenceScore":{"type":"number"},"failureCodes":{"type":"array","items":{"type":"string"}}}},"VerificationApiError":{"allOf":[{"$ref":"#/components/schemas/ApiError"},{"type":"object","required":["error"],"properties":{"error":{"type":"string","enum":["INVALID_JSON","VALIDATION_ERROR","SERVICE_UNAVAILABLE","INTERNAL_ERROR","CREATE_FAILED","VERIFICATION_SESSION_NOT_FOUND","VERIFICATION_INVALID_TOKEN","VERIFICATION_EXPIRED","VERIFICATION_STORAGE_NOT_CONFIGURED","LIVENESS_SERVICE_UNAVAILABLE","LIVENESS_REQUEST_INVALID","API_KEY_MISSING","API_KEY_INVALID"],"description":"Machine-readable code for this HTTP status on this operation"}}}]},"DashboardApiError":{"allOf":[{"$ref":"#/components/schemas/ApiError"},{"type":"object","required":["error"],"properties":{"error":{"type":"string","enum":["INVALID_JSON","VALIDATION_ERROR","UNAUTHENTICATED","SERVICE_UNAVAILABLE","VERIFICATION_SESSION_NOT_FOUND","VERIFICATION_ASSET_NOT_FOUND","VERIFICATION_STORAGE_NOT_CONFIGURED","INTERNAL_ERROR"],"description":"Machine-readable code for this HTTP status on this operation"}}}]},"OrganizationApiKeySummary":{"type":"object","required":["id","keyType","environment","keyPrefix","label","createdAt","lastUsedAt","revokedAt"],"properties":{"id":{"type":"string","format":"uuid"},"keyType":{"type":"string","enum":["publishable","secret"]},"environment":{"type":"string","enum":["test","live"]},"keyPrefix":{"type":"string","description":"First ~16 characters of the minted key (e.g. `sk_live_2fce…`). Full value is only returned at mint time."},"label":{"type":"string","nullable":true},"createdAt":{"type":"string","format":"date-time"},"lastUsedAt":{"type":"string","format":"date-time","nullable":true},"revokedAt":{"type":"string","format":"date-time","nullable":true}}},"ListApiKeysOk":{"type":"object","required":["ok","keys"],"properties":{"ok":{"type":"boolean","enum":[true]},"keys":{"type":"array","items":{"$ref":"#/components/schemas/OrganizationApiKeySummary"}}}},"MintApiKeyPairRequest":{"type":"object","required":["environment"],"properties":{"environment":{"type":"string","enum":["test","live"]},"label":{"type":"string","minLength":1,"maxLength":128,"description":"Human-readable hint (e.g. `backend-prod-2026`)."}}},"MintApiKeyPairOk":{"type":"object","required":["ok","publishable","secret"],"description":"Both raw key values are returned exactly once — store the secret immediately.","properties":{"ok":{"type":"boolean","enum":[true]},"publishable":{"type":"object","required":["raw","summary"],"properties":{"raw":{"type":"string","description":"Full publishable key (shown only once)."},"summary":{"$ref":"#/components/schemas/OrganizationApiKeySummary"}}},"secret":{"type":"object","required":["raw","summary"],"properties":{"raw":{"type":"string","description":"Full secret key (shown only once)."},"summary":{"$ref":"#/components/schemas/OrganizationApiKeySummary"}}}}},"RevokeApiKeyOk":{"type":"object","required":["ok","key"],"properties":{"ok":{"type":"boolean","enum":[true]},"key":{"$ref":"#/components/schemas/OrganizationApiKeySummary"}}}},"securitySchemes":{"tenantBearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"sk_<env>_<random>","description":"Secret API key minted from the dashboard. Required on `POST /api/verification/sessions`. Publishable keys (`pk_…`) are for the SDK only and are NOT accepted here."}}},"paths":{"/api/auth/login":{"post":{"tags":["Auth"],"operationId":"login","summary":"Sign in","description":"Validates credentials and sets httpOnly session cookie `kyc_session` (not modeled in OpenAPI).","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/LoginRequest"}}}},"responses":{"200":{"description":"Success; session cookie set","content":{"application/json":{"schema":{"$ref":"#/components/schemas/LoginOk"}}}},"400":{"description":"Invalid JSON (`INVALID_JSON`) or request validation (`VALIDATION_ERROR`, often with `fieldErrors`)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Login400Error"}}}},"401":{"description":"Invalid credentials (`INVALID_CREDENTIALS`)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Login401Error"}}}},"503":{"description":"No database URL (`SERVICE_UNAVAILABLE`) or session signing not configured (`SESSION_NOT_CONFIGURED`)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Login503Error"}}}}}}},"/api/auth/signup":{"post":{"tags":["Auth"],"operationId":"signup","summary":"Register account","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SignupRequest"}}}},"responses":{"201":{"description":"User created","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SignupOk"}}}},"400":{"description":"Invalid JSON (`INVALID_JSON`) or validation (`VALIDATION_ERROR`, often with `fieldErrors`)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Signup400Error"}}}},"409":{"description":"Unique email conflict (`EMAIL_ALREADY_REGISTERED`)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Signup409Error"}}}},"500":{"description":"Insert returned no row (`CREATE_FAILED`) or unexpected DB failure (`INTERNAL_ERROR`)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Signup500Error"}}}},"503":{"description":"No database URL (`SERVICE_UNAVAILABLE`) or missing tables (`DATABASE_SCHEMA_MISSING`)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Signup503Error"}}}}}}},"/api/auth/logout":{"post":{"tags":["Auth"],"operationId":"logout","summary":"Sign out","description":"Clears session cookie `kyc_session`.","responses":{"200":{"description":"Logged out","content":{"application/json":{"schema":{"$ref":"#/components/schemas/LogoutOk"}}}}}}},"/api/organizations":{"get":{"tags":["Organizations"],"operationId":"listOrganizations","summary":"List organizations for current user","description":"Requires session cookie `kyc_session`. Returns organizations the user is a member of.","responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OrganizationsListOk"}}}},"401":{"description":"Not signed in (`UNAUTHENTICATED`)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OrganizationsGet401Error"}}}},"500":{"description":"Unexpected failure (`INTERNAL_ERROR`)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OrganizationsGet500Error"}}}},"503":{"description":"No database (`SERVICE_UNAVAILABLE`) or migrations missing (`DATABASE_SCHEMA_MISSING`)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OrganizationsGet503Error"}}}}}},"post":{"tags":["Organizations"],"operationId":"createOrganization","summary":"Create organization","description":"Creates an organization and an owner membership for the current user. `customer_id` must be unique.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateOrganizationRequest"}}}},"responses":{"201":{"description":"Created","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateOrganizationOk"}}}},"400":{"description":"Invalid JSON or validation","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OrganizationsPost400Error"}}}},"401":{"description":"Not signed in (`UNAUTHENTICATED`)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OrganizationsPost401Error"}}}},"409":{"description":"Customer ID already taken (`CUSTOMER_ID_TAKEN`)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OrganizationsPost409Error"}}}},"500":{"description":"Create failed (`CREATE_FAILED`) or internal error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OrganizationsPost500Error"}}}},"503":{"description":"No database or migrations missing","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OrganizationsPost503Error"}}}}}}},"/api/health":{"get":{"tags":["System"],"operationId":"health","summary":"Health check","responses":{"200":{"description":"Healthy or degraded (see body.status)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HealthResponse"}}}},"500":{"description":"Unhandled exception in route (`INTERNAL_ERROR`)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Health500Error"}}}},"503":{"description":"Unhealthy","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HealthResponse"}}}}}}},"/api/verification/sessions":{"post":{"tags":["Verification"],"operationId":"createVerificationSession","summary":"Create verification session","security":[{"tenantBearerAuth":[]}],"description":"Server-to-server endpoint. Authenticate with `Authorization: Bearer sk_<env>_<random>`. Returns `sessionId`, `clientSecret`, and `hostedUrl`. Redirect the end-user to `hostedUrl`, or pass `sessionId + clientSecret` to the `@uniicy/kyc-widget` SDK. Optional body fields: `externalUserId`, `returnUrl`, `webhookUrl`, `webhookSecret`, `metadata`.","requestBody":{"required":false,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateVerificationSessionRequest"}}}},"responses":{"201":{"description":"Session created","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateVerificationSessionOk"}}}},"400":{"description":"Invalid JSON or validation (`INVALID_JSON`, `VALIDATION_ERROR`)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/VerificationApiError"}}}},"401":{"description":"Missing or invalid API key (`API_KEY_MISSING`, `API_KEY_INVALID`)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/VerificationApiError"}}}},"500":{"description":"Create failed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/VerificationApiError"}}}},"503":{"description":"Database unavailable","content":{"application/json":{"schema":{"$ref":"#/components/schemas/VerificationApiError"}}}}}}},"/api/verification/sessions/{sessionId}":{"parameters":[{"name":"sessionId","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"get":{"tags":["Verification"],"operationId":"getVerificationSession","summary":"Get verification session","description":"Requires `X-Verification-Token` or `?token=` matching the session.","responses":{"200":{"description":"Current session state and uploaded assets","content":{"application/json":{"schema":{"$ref":"#/components/schemas/GetVerificationSessionOk"}}}},"401":{"description":"Missing or invalid token","content":{"application/json":{"schema":{"$ref":"#/components/schemas/VerificationApiError"}}}},"404":{"description":"Session not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/VerificationApiError"}}}},"410":{"description":"Session expired","content":{"application/json":{"schema":{"$ref":"#/components/schemas/VerificationApiError"}}}},"503":{"description":"Database unavailable","content":{"application/json":{"schema":{"$ref":"#/components/schemas/VerificationApiError"}}}}}},"patch":{"tags":["Verification"],"operationId":"patchVerificationSession","summary":"Set document type","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PatchVerificationSessionRequest"}}}},"responses":{"200":{"description":"Updated session","content":{"application/json":{"schema":{"$ref":"#/components/schemas/GetVerificationSessionOk"}}}},"400":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/VerificationApiError"}}}},"401":{"description":"Invalid token","content":{"application/json":{"schema":{"$ref":"#/components/schemas/VerificationApiError"}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/VerificationApiError"}}}},"410":{"description":"Expired","content":{"application/json":{"schema":{"$ref":"#/components/schemas/VerificationApiError"}}}},"503":{"description":"Database unavailable","content":{"application/json":{"schema":{"$ref":"#/components/schemas/VerificationApiError"}}}}}}},"/api/verification/sessions/{sessionId}/presign":{"parameters":[{"name":"sessionId","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"post":{"tags":["Verification"],"operationId":"presignVerificationUpload","summary":"Presign document upload (PUT to MinIO/S3)","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PresignVerificationUploadRequest"}}}},"responses":{"200":{"description":"Presigned PUT URL","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PresignVerificationUploadOk"}}}},"400":{"description":"Validation or wrong capture phase","content":{"application/json":{"schema":{"$ref":"#/components/schemas/VerificationApiError"}}}},"401":{"description":"Invalid token","content":{"application/json":{"schema":{"$ref":"#/components/schemas/VerificationApiError"}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/VerificationApiError"}}}},"410":{"description":"Expired","content":{"application/json":{"schema":{"$ref":"#/components/schemas/VerificationApiError"}}}},"500":{"description":"Internal error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/VerificationApiError"}}}},"503":{"description":"Storage or database unavailable","content":{"application/json":{"schema":{"$ref":"#/components/schemas/VerificationApiError"}}}}}}},"/api/verification/sessions/{sessionId}/upload-complete":{"parameters":[{"name":"sessionId","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"post":{"tags":["Verification"],"operationId":"completeVerificationUpload","summary":"Confirm upload and run analysis","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CompleteVerificationUploadRequest"}}}},"responses":{"200":{"description":"Asset recorded; analysis started or completed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CompleteVerificationUploadOk"}}}},"400":{"description":"Validation or object mismatch","content":{"application/json":{"schema":{"$ref":"#/components/schemas/VerificationApiError"}}}},"401":{"description":"Invalid token","content":{"application/json":{"schema":{"$ref":"#/components/schemas/VerificationApiError"}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/VerificationApiError"}}}},"410":{"description":"Expired","content":{"application/json":{"schema":{"$ref":"#/components/schemas/VerificationApiError"}}}},"500":{"description":"Internal error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/VerificationApiError"}}}},"503":{"description":"Storage or database unavailable","content":{"application/json":{"schema":{"$ref":"#/components/schemas/VerificationApiError"}}}}}}},"/api/verification/sessions/{sessionId}/liveness/init":{"parameters":[{"name":"sessionId","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"post":{"tags":["Verification"],"operationId":"initVerificationLiveness","summary":"Mint liveness request id (anti-replay)","description":"Requires `X-Verification-Token` (or `?token=`). ID and proof-of-address uploads must be finished (`capturePhase === \"complete\"`).","responses":{"201":{"description":"Pending liveness row created; use `requestId` for complete","content":{"application/json":{"schema":{"$ref":"#/components/schemas/LivenessInitOk"}}}},"400":{"description":"Validation (`VALIDATION_ERROR`) e.g. capture not finished, or insert conflict","content":{"application/json":{"schema":{"$ref":"#/components/schemas/VerificationApiError"}}}},"401":{"description":"Invalid token","content":{"application/json":{"schema":{"$ref":"#/components/schemas/VerificationApiError"}}}},"404":{"description":"Session not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/VerificationApiError"}}}},"410":{"description":"Expired","content":{"application/json":{"schema":{"$ref":"#/components/schemas/VerificationApiError"}}}},"500":{"description":"Internal error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/VerificationApiError"}}}},"503":{"description":"Database unavailable","content":{"application/json":{"schema":{"$ref":"#/components/schemas/VerificationApiError"}}}}}}},"/api/verification/sessions/{sessionId}/liveness/presign-video":{"parameters":[{"name":"sessionId","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"post":{"tags":["Verification"],"operationId":"presignVerificationLivenessVideo","summary":"Presign PUT for optional liveness screen recording","description":"Requires pending `requestId` from init. Client uploads WebM/MP4 blob then calls complete with `videoS3Key` + `videoByteSize`.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/LivenessPresignVideoRequest"}}}},"responses":{"200":{"description":"Presigned upload URL and object key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/LivenessPresignVideoOk"}}}},"400":{"description":"Validation or invalid liveness request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/VerificationApiError"}}}},"401":{"description":"Invalid token","content":{"application/json":{"schema":{"$ref":"#/components/schemas/VerificationApiError"}}}},"404":{"description":"Session not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/VerificationApiError"}}}},"410":{"description":"Expired","content":{"application/json":{"schema":{"$ref":"#/components/schemas/VerificationApiError"}}}},"500":{"description":"Internal error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/VerificationApiError"}}}},"503":{"description":"Database or object storage unavailable","content":{"application/json":{"schema":{"$ref":"#/components/schemas/VerificationApiError"}}}}}}},"/api/verification/sessions/{sessionId}/liveness/complete":{"parameters":[{"name":"sessionId","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"post":{"tags":["Verification"],"operationId":"completeVerificationLiveness","summary":"Submit probe payload and finalize liveness","description":"Body includes `token` and the same `requestId` from init. Server forwards to internal liveness validator (HMAC). Optional `videoS3Key` links a prior presign-video upload.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/LivenessCompleteRequest"}}}},"responses":{"200":{"description":"Validator result","content":{"application/json":{"schema":{"$ref":"#/components/schemas/LivenessCompleteOk"}}}},"400":{"description":"Validation (`VALIDATION_ERROR`), unknown or reused `requestId` (`LIVENESS_REQUEST_INVALID`)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/VerificationApiError"}}}},"401":{"description":"Invalid token","content":{"application/json":{"schema":{"$ref":"#/components/schemas/VerificationApiError"}}}},"404":{"description":"Session not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/VerificationApiError"}}}},"410":{"description":"Expired","content":{"application/json":{"schema":{"$ref":"#/components/schemas/VerificationApiError"}}}},"500":{"description":"Internal error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/VerificationApiError"}}}},"503":{"description":"Database unavailable or liveness validator unreachable (`LIVENESS_SERVICE_UNAVAILABLE`)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/VerificationApiError"}}}}}}},"/api/dashboard/verifications":{"get":{"tags":["Dashboard"],"operationId":"listDashboardVerifications","summary":"List verification sessions (dashboard)","description":"Requires authenticated session cookie. Returns each session with latest liveness attempt summary.","responses":{"200":{"description":"List of sessions","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DashboardVerificationsListOk"}}}},"401":{"description":"Not logged in","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DashboardApiError"}}}},"503":{"description":"Database unavailable","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DashboardApiError"}}}}}}},"/api/dashboard/verifications/{sessionId}":{"parameters":[{"name":"sessionId","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"get":{"tags":["Dashboard"],"operationId":"getDashboardVerificationDetail","summary":"Verification session detail (dashboard)","description":"Session row, document assets, all liveness attempts, and append-only audit events.","responses":{"200":{"description":"Detail payload","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DashboardVerificationDetailOk"}}}},"401":{"description":"Not logged in","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DashboardApiError"}}}},"404":{"description":"Session not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DashboardApiError"}}}},"503":{"description":"Database unavailable","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DashboardApiError"}}}}}}},"/api/dashboard/verifications/{sessionId}/review":{"parameters":[{"name":"sessionId","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"post":{"tags":["Dashboard"],"operationId":"submitDashboardVerificationReview","summary":"Manual approve or reject","description":"Persists decision, audit row, and optionally POSTs to `VERIFICATION_OUTCOME_WEBHOOK_URL`.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/DashboardVerificationReviewRequest"}}}},"responses":{"200":{"description":"Review saved","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DashboardVerificationReviewOk"}}}},"400":{"description":"Invalid JSON or validation","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DashboardApiError"}}}},"401":{"description":"Not logged in","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DashboardApiError"}}}},"404":{"description":"Session not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DashboardApiError"}}}},"500":{"description":"Internal error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DashboardApiError"}}}},"503":{"description":"Database unavailable","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DashboardApiError"}}}}}}},"/api/dashboard/verifications/{sessionId}/assets/{assetId}/reanalyze":{"parameters":[{"name":"sessionId","in":"path","required":true,"schema":{"type":"string","format":"uuid"}},{"name":"assetId","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"post":{"tags":["Dashboard"],"operationId":"reanalyzeDashboardVerificationAsset","summary":"Re-run document analysis for one asset","description":"Deletes the prior analysis row, records `admin_document_reanalyze`, and runs the same pipeline as upload. For ID images, automated session rejection and outcome webhooks are not re-fired.","responses":{"200":{"description":"Analysis re-run completed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DashboardVerificationAssetReanalyzeOk"}}}},"401":{"description":"Not logged in","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DashboardApiError"}}}},"404":{"description":"Session or asset not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DashboardApiError"}}}},"500":{"description":"Internal error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DashboardApiError"}}}},"503":{"description":"Database or storage unavailable","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DashboardApiError"}}}}}}},"/api/dashboard/organizations/{organizationId}/api-keys":{"parameters":[{"name":"organizationId","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"get":{"tags":["Dashboard"],"operationId":"listOrganizationApiKeys","summary":"List API keys for an organization","description":"Returns every key (active + revoked) for the organization. Raw key values are NOT returned — only the stored prefix and metadata.","responses":{"200":{"description":"Keys","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ListApiKeysOk"}}}},"401":{"description":"Not logged in","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DashboardApiError"}}}},"403":{"description":"Authenticated user is not a member of this organization","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DashboardApiError"}}}},"503":{"description":"Database unavailable","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DashboardApiError"}}}}}},"post":{"tags":["Dashboard"],"operationId":"mintOrganizationApiKeyPair","summary":"Mint a new publishable + secret key pair","description":"Generates a matched pair (`pk_<env>_…` + `sk_<env>_…`). Both raw values are returned exactly once — store them immediately.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/MintApiKeyPairRequest"}}}},"responses":{"201":{"description":"Minted","content":{"application/json":{"schema":{"$ref":"#/components/schemas/MintApiKeyPairOk"}}}},"400":{"description":"Invalid JSON or validation","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DashboardApiError"}}}},"401":{"description":"Not logged in","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DashboardApiError"}}}},"403":{"description":"Authenticated user is not a member of this organization","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DashboardApiError"}}}},"500":{"description":"Mint failed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DashboardApiError"}}}}}}},"/api/dashboard/organizations/{organizationId}/api-keys/{keyId}":{"parameters":[{"name":"organizationId","in":"path","required":true,"schema":{"type":"string","format":"uuid"}},{"name":"keyId","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"delete":{"tags":["Dashboard"],"operationId":"revokeOrganizationApiKey","summary":"Revoke an API key","description":"Soft-delete: sets `revokedAt`; the row is retained for audit. The key stops authenticating immediately on the next request.","responses":{"200":{"description":"Revoked","content":{"application/json":{"schema":{"$ref":"#/components/schemas/RevokeApiKeyOk"}}}},"401":{"description":"Not logged in","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DashboardApiError"}}}},"403":{"description":"Authenticated user is not a member of this organization","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DashboardApiError"}}}},"404":{"description":"Key not found or already revoked","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DashboardApiError"}}}},"500":{"description":"Internal error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DashboardApiError"}}}}}}},"/api/dashboard/retraining/notify":{"post":{"tags":["Dashboard"],"operationId":"notifyRetrainingBatch","summary":"Record false-positive batch for external ML pipeline (stub)","description":"Writes an audit event of type `retraining_batch`. Does not export media.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RetrainingNotifyRequest"}}}},"responses":{"200":{"description":"Recorded","content":{"application/json":{"schema":{"$ref":"#/components/schemas/RetrainingNotifyOk"}}}},"400":{"description":"Invalid JSON or validation","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DashboardApiError"}}}},"401":{"description":"Not logged in","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DashboardApiError"}}}},"500":{"description":"Internal error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DashboardApiError"}}}},"503":{"description":"Database unavailable","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DashboardApiError"}}}}}}}}}