{"openapi":"3.1.0","info":{"title":"IDz KYC API","version":"1.0.0"},"paths":{"/v1/verifications/document":{"post":{"tags":["Verifications"],"summary":"Verify an ID document (front + back). No biometric.","description":"Queue a document-only verification.\n\nReturns 202 with `status: \"in_progress\"` once the upload is persisted\nand the work is scheduled. The actual OCR pipeline runs in the\nbackground; poll `GET /v1/verifications/{id}` until status is one of\n`completed`, `rejected`, or `failed`.\n\nSet the `Idempotency-Key` header to make the request safe to retry —\na second call with the same key replays the cached 202 response.","operationId":"create_document_verification_v1_verifications_document_post","requestBody":{"content":{"multipart/form-data":{"schema":{"$ref":"#/components/schemas/Body_create_document_verification_v1_verifications_document_post"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Verification"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/verifications/identity":{"post":{"tags":["Verifications"],"summary":"Verify identity: ID document + selfie face match.","description":"Queue an identity verification (document + face match).\n\nReturns 202 with `status: \"in_progress\"`. Poll `GET /v1/verifications/{id}`\nuntil status is one of `completed`, `rejected`, or `failed`.\n`checks.liveness` will be null in the final response (no video).\n\nSet the `Idempotency-Key` header to make the request safe to retry.","operationId":"create_identity_verification_v1_verifications_identity_post","requestBody":{"content":{"multipart/form-data":{"schema":{"$ref":"#/components/schemas/Body_create_identity_verification_v1_verifications_identity_post"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Verification"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/verifications/identity_live":{"post":{"tags":["Verifications"],"summary":"Verify identity with passive liveness (ID + selfie + video).","description":"Queue the strongest assurance verification (document + face match + liveness).\n\nReturns 202 with `status: \"in_progress\"`. Poll `GET /v1/verifications/{id}`\nuntil status is one of `completed`, `rejected`, or `failed`.\n\nSet the `Idempotency-Key` header to make the request safe to retry.","operationId":"create_identity_live_verification_v1_verifications_identity_live_post","requestBody":{"content":{"multipart/form-data":{"schema":{"$ref":"#/components/schemas/Body_create_identity_live_verification_v1_verifications_identity_live_post"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Verification"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/verifications":{"get":{"tags":["Verifications"],"summary":"List verifications for the calling org (paginated).","description":"Returns the calling org's verifications, newest first.\n\nPagination is cursor-based on `(created_at DESC, session_id DESC)`. The\nresponse includes `has_more` and `next_cursor`; pass `next_cursor` back\nas the `cursor` query param to walk further into the past.\n\n`document.fields` is omitted from list rows (use the detail endpoint\nto fetch full OCR fields).","operationId":"list_verifications_v1_verifications_get","parameters":[{"name":"status","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Filter to one status: in_progress | completed | rejected | failed","title":"Status"},"description":"Filter to one status: in_progress | completed | rejected | failed"},{"name":"created_after","in":"query","required":false,"schema":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"description":"ISO timestamp lower bound (inclusive)","title":"Created After"},"description":"ISO timestamp lower bound (inclusive)"},{"name":"created_before","in":"query","required":false,"schema":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"description":"ISO timestamp upper bound (inclusive)","title":"Created Before"},"description":"ISO timestamp upper bound (inclusive)"},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","maximum":100,"minimum":1,"default":20,"title":"Limit"}},{"name":"cursor","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Opaque cursor returned as `next_cursor` from a prior page","title":"Cursor"},"description":"Opaque cursor returned as `next_cursor` from a prior page"}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/VerificationList"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/verifications/counts":{"get":{"tags":["Verifications"],"summary":"Counts strip for the dashboard's Verifications screen.","description":"Aggregates the dashboard's Verifications tab header renders:\n\n  in_progress        — sessions currently mid-pipeline\n  completed_today    — terminal status='completed' since 00:00 UTC\n  rejected_today     — terminal status='rejected' since 00:00 UTC\n  review_queue       — sessions with at least one open risk_flag and\n                       no review_decision row yet\n  review_over_sla    — subset of review_queue older than 1 hour\n  failed_system      — terminal status='failed' (server abandoned /\n                       internal error / fatal pipeline) in the\n                       last 24h\n  *_trend_pct        — % change vs the same window the previous day\n\nSingle DB round-trip via FILTER aggregates. Trend percentages come\nfrom the same query against the previous-day window.","operationId":"get_verifications_counts_v1_verifications_counts_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/v1/verifications/{verification_id}":{"get":{"tags":["Verifications"],"summary":"Retrieve a verification by id.","description":"Full Verification including `document.fields`. 404 for unknown or\ncross-org ids — the dependency hides existence.","operationId":"get_verification_v1_verifications__verification_id__get","parameters":[{"name":"verification_id","in":"path","required":true,"schema":{"type":"string","title":"Verification Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Verification"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/verifications/{verification_id}/audit":{"get":{"tags":["Verifications"],"summary":"Audit trail for a verification.","description":"Append-only audit log for this verification. Internal user_ids are\nNOT exposed — only `actor_kind` (api / client / system) so the caller\ncan distinguish their own POST events from platform admin actions.","operationId":"get_verification_audit_v1_verifications__verification_id__audit_get","parameters":[{"name":"verification_id","in":"path","required":true,"schema":{"type":"string","title":"Verification Id"}},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","maximum":200,"minimum":1,"default":50,"title":"Limit"}},{"name":"offset","in":"query","required":false,"schema":{"type":"integer","minimum":0,"default":0,"title":"Offset"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/VerificationAuditEntry"},"title":"Response Get Verification Audit V1 Verifications  Verification Id  Audit Get"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/verifications/{verification_id}/artifacts":{"get":{"tags":["Verifications"],"summary":"List artifacts for a verification, grouped by purpose.","description":"Return all artifacts for the verification, grouped into uploads /\ndetections / crops / face_match / liveness so result-screen UIs can\nrender an Artifacts tab without reverse-engineering filename patterns.\n\nEach item's `url` points at the existing\n`/v1/verifications/{id}/artifacts/{artifact_id}` streaming endpoint —\nfetch with the same API key used for this listing call. Cross-org or\nunknown verification ids return 404 via `org_scoped_verification`.","operationId":"list_verification_artifacts_v1_verifications__verification_id__artifacts_get","parameters":[{"name":"verification_id","in":"path","required":true,"schema":{"type":"string","title":"Verification Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/VerificationArtifactsCatalog"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/verifications/{verification_id}/artifacts/{artifact_id}":{"get":{"tags":["Verifications"],"summary":"Stream a verification artifact (image / video).","description":"Return the stored artifact bytes. Org-scoping is enforced by\n`org_scoped_verification` — the artifact must belong to a session\nowned by the caller's org. Cross-org artifact ids return 404 (not 403)\nto avoid leaking existence.","operationId":"get_verification_artifact_v1_verifications__verification_id__artifacts__artifact_id__get","parameters":[{"name":"artifact_id","in":"path","required":true,"schema":{"type":"string","title":"Artifact Id"}},{"name":"verification_id","in":"path","required":true,"schema":{"type":"string","title":"Verification Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/webhook_endpoints":{"post":{"tags":["Webhooks"],"summary":"Register a new webhook endpoint.","description":"Create a webhook endpoint for the calling org. The response\nincludes the `signing_secret` exactly **once** — store it securely;\nuse it to verify the `X-IDz-Signature` header on delivered events.","operationId":"create_webhook_endpoint_v1_webhook_endpoints_post","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookEndpointCreate"}}}},"responses":{"201":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookEndpointWithSecret"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"get":{"tags":["Webhooks"],"summary":"List webhook endpoints for the calling org.","operationId":"list_webhook_endpoints_v1_webhook_endpoints_get","parameters":[{"name":"limit","in":"query","required":false,"schema":{"type":"integer","maximum":200,"minimum":1,"default":50,"title":"Limit"}},{"name":"offset","in":"query","required":false,"schema":{"type":"integer","minimum":0,"default":0,"title":"Offset"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/WebhookEndpointResponse"},"title":"Response List Webhook Endpoints V1 Webhook Endpoints Get"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/webhook_endpoints/{endpoint_id}":{"get":{"tags":["Webhooks"],"summary":"Retrieve a webhook endpoint by id.","operationId":"get_webhook_endpoint_v1_webhook_endpoints__endpoint_id__get","parameters":[{"name":"endpoint_id","in":"path","required":true,"schema":{"type":"string","title":"Endpoint Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookEndpointResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"patch":{"tags":["Webhooks"],"summary":"Update a webhook endpoint.","operationId":"update_webhook_endpoint_v1_webhook_endpoints__endpoint_id__patch","parameters":[{"name":"endpoint_id","in":"path","required":true,"schema":{"type":"string","title":"Endpoint Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookEndpointUpdate"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookEndpointResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"delete":{"tags":["Webhooks"],"summary":"Delete a webhook endpoint.","operationId":"delete_webhook_endpoint_v1_webhook_endpoints__endpoint_id__delete","parameters":[{"name":"endpoint_id","in":"path","required":true,"schema":{"type":"string","title":"Endpoint Id"}}],"responses":{"204":{"description":"Successful Response"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/webhook_endpoints/{endpoint_id}/rotate_secret":{"post":{"tags":["Webhooks"],"summary":"Rotate the signing secret for an endpoint.","description":"Generate a new `signing_secret` and return it. The old secret is\ninvalidated immediately — schedule your secret-update window\naccordingly.","operationId":"rotate_secret_v1_webhook_endpoints__endpoint_id__rotate_secret_post","parameters":[{"name":"endpoint_id","in":"path","required":true,"schema":{"type":"string","title":"Endpoint Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookEndpointWithSecret"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/webhook_endpoints/{endpoint_id}/deliveries":{"get":{"tags":["Webhooks"],"summary":"List delivery attempts for a webhook endpoint.","description":"Newest first. Use a small limit + offset for paging; cursor-based\npagination here would be overkill for a delivery log.","operationId":"list_deliveries_v1_webhook_endpoints__endpoint_id__deliveries_get","parameters":[{"name":"endpoint_id","in":"path","required":true,"schema":{"type":"string","title":"Endpoint Id"}},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","maximum":200,"minimum":1,"default":50,"title":"Limit"}},{"name":"offset","in":"query","required":false,"schema":{"type":"integer","minimum":0,"default":0,"title":"Offset"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/WebhookDeliveryResponse"},"title":"Response List Deliveries V1 Webhook Endpoints  Endpoint Id  Deliveries Get"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/webhook_endpoints/{endpoint_id}/deliveries/{delivery_id}":{"get":{"tags":["Webhooks"],"summary":"Retrieve one delivery attempt.","operationId":"get_delivery_v1_webhook_endpoints__endpoint_id__deliveries__delivery_id__get","parameters":[{"name":"endpoint_id","in":"path","required":true,"schema":{"type":"string","title":"Endpoint Id"}},{"name":"delivery_id","in":"path","required":true,"schema":{"type":"string","title":"Delivery Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookDeliveryResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/webhook_endpoints/{endpoint_id}/deliveries/{delivery_id}/redeliver":{"post":{"tags":["Webhooks"],"summary":"Manually retry a failed delivery.","description":"Resets the row to `pending`, bumps `attempt`, schedules a fresh\nHTTP attempt as a background task. Returns immediately with the\nupdated row.\n\nCustomer use case: receiver was down when the original delivery\nfired, status='failed' now, customer wants to retry without waiting\nfor the future auto-retry poller.","operationId":"redeliver_v1_webhook_endpoints__endpoint_id__deliveries__delivery_id__redeliver_post","parameters":[{"name":"endpoint_id","in":"path","required":true,"schema":{"type":"string","title":"Endpoint Id"}},{"name":"delivery_id","in":"path","required":true,"schema":{"type":"string","title":"Delivery Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookDeliveryResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/audit":{"get":{"tags":["audit"],"summary":"List audit events (dashboard JWT only).","operationId":"list_audit_events_v1_audit_get","parameters":[{"name":"from","in":"query","required":false,"schema":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"From"}},{"name":"to","in":"query","required":false,"schema":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"To"}},{"name":"category","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Category"}},{"name":"actor_user_id","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Actor User Id"}},{"name":"action","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Action"}},{"name":"sensitive_only","in":"query","required":false,"schema":{"type":"boolean","default":false,"title":"Sensitive Only"}},{"name":"cursor","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Cursor"}},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","maximum":200,"minimum":1,"default":50,"title":"Limit"}},{"name":"org_id","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Platform admins only","title":"Org Id"},"description":"Platform admins only"}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AuditListOut"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/audit/counts":{"get":{"tags":["audit"],"summary":"Audit-log count strip for the dashboard header.","operationId":"get_audit_counts_v1_audit_counts_get","parameters":[{"name":"org_id","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Platform admins only","title":"Org Id"},"description":"Platform admins only"}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AuditCountsOut"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/audit/export.csv":{"get":{"tags":["audit"],"summary":"Stream the audit log as CSV (dashboard JWT only).","description":"Stream matching audit rows as CSV. Paginates internally in\n1000-row chunks so the response object stays bounded regardless of\nhow many rows match. The dashboard hits this with the same filter\nstate the user is viewing; ops curls it for SIEM ingest.\n\nHeader `content-disposition: attachment; filename=audit-YYYYMMDD.csv`\nmakes browsers offer Save-As instead of inline render.","operationId":"export_audit_csv_v1_audit_export_csv_get","parameters":[{"name":"from","in":"query","required":false,"schema":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"From"}},{"name":"to","in":"query","required":false,"schema":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"To"}},{"name":"category","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Category"}},{"name":"actor_user_id","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Actor User Id"}},{"name":"action","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Action"}},{"name":"sensitive_only","in":"query","required":false,"schema":{"type":"boolean","default":false,"title":"Sensitive Only"}},{"name":"org_id","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Platform admins only","title":"Org Id"},"description":"Platform admins only"}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/audit/facets":{"get":{"tags":["audit"],"summary":"Distinct actor + action values for filter dropdowns.","description":"Return distinct actor + action values seen in the last `days`\ndays, scoped to caller's org. Powers the dashboard's filter\nselects. Capped at 200 entries each so the response is bounded\neven on noisy orgs.\n\nWindow-scoped (not all-time) so the query touches the\n`ix_audit_logs_timestamp` index instead of full-table-scanning the\naudit table at 7-year retention.\n\nCategories are derived from the action set client-side via\n`category_for`, so we don't query the DB for them — the response\nincludes the known list directly.","operationId":"get_audit_facets_v1_audit_facets_get","parameters":[{"name":"org_id","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Platform admins only","title":"Org Id"},"description":"Platform admins only"},{"name":"days","in":"query","required":false,"schema":{"type":"integer","maximum":365,"minimum":1,"default":30,"title":"Days"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AuditFacetsOut"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/risk-flags":{"get":{"tags":["risk-flags"],"summary":"List open risk flags (dashboard JWT only).","operationId":"list_risk_flags_v1_risk_flags_get","parameters":[{"name":"kind","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Kind"}},{"name":"source","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"auto | manual","title":"Source"},"description":"auto | manual"},{"name":"severity","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"info | warning | critical","title":"Severity"},"description":"info | warning | critical"},{"name":"include_resolved","in":"query","required":false,"schema":{"type":"boolean","description":"Include resolved flags","default":false,"title":"Include Resolved"},"description":"Include resolved flags"},{"name":"cursor","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Cursor"}},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","maximum":200,"minimum":1,"default":50,"title":"Limit"}},{"name":"org_id","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Platform admins only","title":"Org Id"},"description":"Platform admins only"}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/RiskFlagListOut"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"post":{"tags":["risk-flags"],"summary":"Raise a manual risk flag against a session.","operationId":"raise_manual_risk_flag_v1_risk_flags_post","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ManualFlagIn"}}}},"responses":{"201":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/RiskFlagOut"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/risk-flags/counts":{"get":{"tags":["risk-flags"],"summary":"Risk-flag counts strip for the dashboard header.","operationId":"get_risk_flag_counts_v1_risk_flags_counts_get","parameters":[{"name":"org_id","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Platform admins only","title":"Org Id"},"description":"Platform admins only"}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/RiskFlagCountsOut"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/risk-flags/{flag_id}/resolve":{"post":{"tags":["risk-flags"],"summary":"Resolve a risk flag with an optional resolution note.","operationId":"resolve_risk_flag_v1_risk_flags__flag_id__resolve_post","parameters":[{"name":"flag_id","in":"path","required":true,"schema":{"type":"string","title":"Flag Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ResolveFlagIn"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/RiskFlagOut"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/reviews":{"get":{"tags":["reviews"],"summary":"List sessions awaiting manual review (dashboard JWT only).","operationId":"list_review_queue_v1_reviews_get","parameters":[{"name":"page","in":"query","required":false,"schema":{"type":"integer","minimum":1,"default":1,"title":"Page"}},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","maximum":200,"minimum":1,"default":20,"title":"Limit"}},{"name":"org_id","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Platform admins only","title":"Org Id"},"description":"Platform admins only"}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ReviewListOut"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/reviews/counts":{"get":{"tags":["reviews"],"summary":"Review-queue counts strip for the dashboard header.","operationId":"get_review_counts_v1_reviews_counts_get","parameters":[{"name":"org_id","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Platform admins only","title":"Org Id"},"description":"Platform admins only"}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ReviewCountsOut"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/reviews/bulk-decide":{"post":{"tags":["reviews"],"summary":"Record the same decision against multiple sessions in one request.","description":"Fan out a single decision across many sessions.\n\nBeats N round-trips for the dashboard's bulk action bar without\nneeding a separate bulk-optimised SQL path: we just iterate over\nthe supplied ids and call `ReviewDecisionService.record_decision`\nfor each, collecting per-row results. Each successful row also\nemits a `review.decided` audit event (consistent with the\nsingle-session endpoint).\n\nAlways returns 200 with per-row results — the dashboard renders\n'X approved, Y errors' from the aggregated counts rather than\nfailing the whole batch on one bad id.\n\nCross-org session ids surface as `error: \"not found\"` (same\nprivacy shape as the single endpoint — never `403: wrong org`).","operationId":"bulk_decide_reviews_v1_reviews_bulk_decide_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/BulkDecisionIn"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BulkDecisionOut"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/reviews/claim-next":{"post":{"tags":["reviews"],"summary":"Atomically claim the oldest unclaimed session in the review queue.","description":"Atomic FIFO claim using `FOR UPDATE SKIP LOCKED`.\n\nSelects the oldest session that:\n  - belongs to caller's org (or `?org_id=` for platform admin)\n  - has at least one open risk flag\n  - has no ReviewDecision row yet\n  - is not currently claimed by another operator\n…and stamps it with `claimed_by=identity.user_id` + `claimed_at=now`\nin a single transaction. Multiple concurrent /claim-next calls\nwon't double-claim — `SKIP LOCKED` makes each picker grab a\ndifferent row. The atomicity is the entire point of this endpoint\n(vs. the dashboard polling the list and picking the first).\n\nReturns 404 (with body `{detail: \"review queue empty\"}`) when no\nmatching session exists. The dashboard renders \"All caught up\"\nin that case rather than treating it as an error.","operationId":"claim_next_review_v1_reviews_claim_next_post","parameters":[{"name":"org_id","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Platform admins only","title":"Org Id"},"description":"Platform admins only"}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ClaimNextOut"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/reviews/{session_id}/decide":{"post":{"tags":["reviews"],"summary":"Record a manual review decision against a session.","operationId":"decide_review_v1_reviews__session_id__decide_post","parameters":[{"name":"session_id","in":"path","required":true,"schema":{"type":"string","title":"Session Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/DecisionIn"}}}},"responses":{"201":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DecisionOut"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/webhooks":{"get":{"tags":["webhooks-dashboard"],"summary":"List webhook endpoints with computed 24h health.","operationId":"list_webhook_endpoints_v1_webhooks_get","parameters":[{"name":"org_id","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Platform admins only","title":"Org Id"},"description":"Platform admins only"}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookEndpointListOut"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"post":{"tags":["webhooks-dashboard"],"summary":"Create a webhook endpoint as a dashboard admin.","description":"Dashboard-side proxy for `POST /v1/webhook_endpoints`. Same\nbehaviour, same response (including the one-shot `signing_secret`),\njust gated by the dashboard session JWT instead of an API key so\nthe org admin can register an endpoint without first minting a\ncustomer-facing key.","operationId":"create_webhook_endpoint_as_dashboard_v1_webhooks_post","parameters":[{"name":"org_id","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Platform admins only","title":"Org Id"},"description":"Platform admins only"}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Payload"}}}},"responses":{"201":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/webhooks/counts":{"get":{"tags":["webhooks-dashboard"],"summary":"Webhook counts strip for the dashboard header.","operationId":"get_webhook_counts_v1_webhooks_counts_get","parameters":[{"name":"org_id","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Platform admins only","title":"Org Id"},"description":"Platform admins only"}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookCountsOut"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/webhooks/deliveries":{"get":{"tags":["webhooks-dashboard"],"summary":"List recent webhook deliveries with status-bucket filter.","operationId":"list_webhook_deliveries_v1_webhooks_deliveries_get","parameters":[{"name":"status_bucket","in":"query","required":false,"schema":{"type":"string","description":"any | 2xx | 4xx | 5xx | retrying | failed","default":"any","title":"Status Bucket"},"description":"any | 2xx | 4xx | 5xx | retrying | failed"},{"name":"endpoint_id","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Endpoint Id"}},{"name":"event_type","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Event Type"}},{"name":"cursor","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Cursor"}},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","maximum":200,"minimum":1,"default":50,"title":"Limit"}},{"name":"org_id","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Platform admins only","title":"Org Id"},"description":"Platform admins only"}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookDeliveryListOut"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/webhooks/{endpoint_id}/pause":{"post":{"tags":["webhooks-dashboard"],"summary":"Pause an endpoint (enabled=false). Idempotent.","operationId":"pause_webhook_endpoint_v1_webhooks__endpoint_id__pause_post","parameters":[{"name":"endpoint_id","in":"path","required":true,"schema":{"type":"string","title":"Endpoint Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookEndpointOut"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/webhooks/{endpoint_id}/resume":{"post":{"tags":["webhooks-dashboard"],"summary":"Resume a paused endpoint (enabled=true). Idempotent.","operationId":"resume_webhook_endpoint_v1_webhooks__endpoint_id__resume_post","parameters":[{"name":"endpoint_id","in":"path","required":true,"schema":{"type":"string","title":"Endpoint Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookEndpointOut"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/webhooks/deliveries/{delivery_id}/redeliver":{"post":{"tags":["webhooks-dashboard"],"summary":"Re-queue a delivery for retry. Audits the operator action.","operationId":"redeliver_webhook_delivery_v1_webhooks_deliveries__delivery_id__redeliver_post","parameters":[{"name":"delivery_id","in":"path","required":true,"schema":{"type":"string","title":"Delivery Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookDeliveryOut"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/webhooks/{endpoint_id}/rotate_secret":{"post":{"tags":["webhooks-dashboard"],"summary":"Rotate an endpoint's signing secret as a dashboard admin.","description":"Dashboard-side proxy for `POST /v1/webhook_endpoints/{id}/rotate_secret`.\nReturns the new `signing_secret` once; the old one is invalidated\nimmediately so schedule the rotation window before calling.","operationId":"rotate_secret_as_dashboard_v1_webhooks__endpoint_id__rotate_secret_post","parameters":[{"name":"endpoint_id","in":"path","required":true,"schema":{"type":"string","title":"Endpoint Id"}},{"name":"org_id","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Platform admins only","title":"Org Id"},"description":"Platform admins only"}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/keys":{"get":{"tags":["keys"],"summary":"List API keys for the caller's org (Convex metadata + local scopes).","description":"Powers the dashboard's API keys screen.\n\nJoins two sources:\n  - Convex `apiKeys:listForOrg` for metadata (name, env, suffix,\n    created_at, last_used_at, revoked_at).\n  - Local `api_key_scopes` table for `permissions` (the per-key\n    scope grants the dashboard manages here).\n\nSplits the result into active (revoked_at == None) and revoked\narrays so the dashboard's two tabs render without re-filtering.\n\nAuth (defence-in-depth):\n  - This route is dashboard-JWT-gated (require_org_admin_or_platform).\n  - The Convex query then independently verifies membership /\n    super-admin via Better Auth. A compromised IDz backend\n    couldn't enumerate other orgs' keys by claim alone.\n\nConvex returns AUTH_FORBIDDEN → we surface 403. Network or\ntransient errors → empty list (fail-closed, logged).","operationId":"list_keys_v1_keys_get","parameters":[{"name":"org_id","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Org Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/KeyListOut"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/keys/scopes/catalog":{"get":{"tags":["keys"],"summary":"The curated set of scope strings that can be granted.","description":"No auth gate — the catalog is documentation-style data, not\norg-specific. The dashboard renders it as the picker source on\nthe 'Edit scopes' modal.","operationId":"get_scope_catalog_v1_keys_scopes_catalog_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ScopeCatalogOut"}}}}}}},"/v1/keys/{api_key_id}/scopes":{"get":{"tags":["keys"],"summary":"List granted scopes for an API key.","operationId":"list_key_scopes_v1_keys__api_key_id__scopes_get","parameters":[{"name":"api_key_id","in":"path","required":true,"schema":{"type":"string","title":"Api Key Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ScopeListOut"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"put":{"tags":["keys"],"summary":"Replace the scope set for an API key.","description":"Idempotent replace. Returns the resulting scope set. Emits a\n`api_key.scopes_changed` audit event (which is in the curated\nSENSITIVE_ACTIONS list — surfaces on the Audit log's 'sensitive\nactions' counter).","operationId":"set_key_scopes_v1_keys__api_key_id__scopes_put","parameters":[{"name":"api_key_id","in":"path","required":true,"schema":{"type":"string","title":"Api Key Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ScopeSetIn"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ScopeListOut"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/members/roles/catalog":{"get":{"tags":["members"],"summary":"List known roles + their permission sets.","operationId":"get_role_catalog_v1_members_roles_catalog_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/RoleCatalogOut"}}}}}}},"/v1/members":{"get":{"tags":["members"],"summary":"List members of the caller's org.","operationId":"list_members_v1_members_get","parameters":[{"name":"include_removed","in":"query","required":false,"schema":{"type":"boolean","default":false,"title":"Include Removed"}},{"name":"org_id","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Org Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/MemberListOut"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"post":{"tags":["members"],"summary":"Add a member to the caller's org.","operationId":"add_member_v1_members_post","parameters":[{"name":"org_id","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Org Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/MemberAddIn"}}}},"responses":{"201":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/MemberOut"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/members/{user_id}/roles":{"put":{"tags":["members"],"summary":"Replace a member's role set.","operationId":"set_member_roles_v1_members__user_id__roles_put","parameters":[{"name":"user_id","in":"path","required":true,"schema":{"type":"string","title":"User Id"}},{"name":"org_id","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Org Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RoleSetIn"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/MemberOut"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/members/{user_id}":{"delete":{"tags":["members"],"summary":"Remove a member from the org. Idempotent; drops their role grants.","operationId":"remove_member_v1_members__user_id__delete","parameters":[{"name":"user_id","in":"path","required":true,"schema":{"type":"string","title":"User Id"}},{"name":"org_id","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Org Id"}}],"responses":{"204":{"description":"Successful Response"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/settings/catalog":{"get":{"tags":["settings"],"summary":"The curated catalog of setting keys + groups.","operationId":"get_settings_catalog_v1_settings_catalog_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SettingsCatalogOut"}}}}}}},"/v1/settings":{"get":{"tags":["settings"],"summary":"All settings for the caller's org (or a specified one for platform admins).","operationId":"list_settings_v1_settings_get","parameters":[{"name":"org_id","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Org Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SettingsMapOut"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/settings/{group}":{"get":{"tags":["settings"],"summary":"Settings within a single group (org / branding / anpdp / …).","operationId":"list_group_settings_v1_settings__group__get","parameters":[{"name":"group","in":"path","required":true,"schema":{"type":"string","title":"Group"}},{"name":"org_id","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Org Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SettingsMapOut"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"put":{"tags":["settings"],"summary":"Replace settings within a group; idempotent. Emits per-key audit events.","operationId":"put_group_settings_v1_settings__group__put","parameters":[{"name":"group","in":"path","required":true,"schema":{"type":"string","title":"Group"}},{"name":"org_id","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Org Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SettingsReplaceIn"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SettingsMapOut"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/settings/branding/upload":{"post":{"tags":["settings"],"summary":"Upload a logo or favicon file; updates branding.{kind}_url server-side.","operationId":"upload_branding_v1_settings_branding_upload_post","parameters":[{"name":"org_id","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Org Id"}}],"requestBody":{"required":true,"content":{"multipart/form-data":{"schema":{"$ref":"#/components/schemas/Body_upload_branding_v1_settings_branding_upload_post"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BrandingUploadOut"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/settings/danger/delete-org":{"post":{"tags":["settings"],"summary":"Audit operator intent to delete the org. Actual cascade is a follow-up PR.","operationId":"delete_org_v1_settings_danger_delete_org_post","parameters":[{"name":"org_id","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Org Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/DeleteOrgIn"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Response Delete Org V1 Settings Danger Delete Org Post"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/search":{"get":{"tags":["search"],"summary":"Search","description":"Search the caller's org for verifications matching `q`.\n\nPriority cascade: session_id prefix → id_number → card_number →\nlicense_number. Stops at the first priority that returns hits.\n`has_more` is true only if the cascade hit the `limit` cap; v1\ndoesn't paginate beyond that (the dropdown shows the top hits).","operationId":"search_v1_search_get","parameters":[{"name":"q","in":"query","required":true,"schema":{"type":"string","description":"Search term (≥ 2 chars)","title":"Q"},"description":"Search term (≥ 2 chars)"},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","maximum":50,"minimum":1,"default":20,"title":"Limit"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SearchResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/notifications":{"get":{"tags":["notifications"],"summary":"List Notifications","description":"Newest-first paginated list. Empty list on no notifications\n(200, not 404). Platform admins without an org binding get an\nempty list — notifications are single-org by design.","operationId":"list_notifications_v1_notifications_get","parameters":[{"name":"limit","in":"query","required":false,"schema":{"type":"integer","maximum":50,"minimum":1,"default":20,"title":"Limit"}},{"name":"cursor","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Cursor"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/NotificationListResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/notifications/unread_count":{"get":{"tags":["notifications"],"summary":"Get Unread Count","description":"Single-int bell badge count. Returns 0 for platform admins\nwithout an org binding (no notifications to count).","operationId":"get_unread_count_v1_notifications_unread_count_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/UnreadCountResponse"}}}}}}},"/v1/notifications/{notification_id}/read":{"post":{"tags":["notifications"],"summary":"Mark Notification Read","description":"Idempotent mark-as-read. 204 on success (also for already-read\nrows that exist). 404 when the id is unknown or belongs to a\ndifferent org — the existence-leak rule the rest of the /v1\nsurface uses.","operationId":"mark_notification_read_v1_notifications__notification_id__read_post","parameters":[{"name":"notification_id","in":"path","required":true,"schema":{"type":"string","title":"Notification Id"}}],"responses":{"204":{"description":"Successful Response"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/notifications/mark_all_read":{"post":{"tags":["notifications"],"summary":"Mark All Notifications Read","description":"Bulk-stamp every unread notification for the caller's org.\nReturns the rows-updated count so the UI can update the badge\nimmediately without a follow-up unread_count call.","operationId":"mark_all_notifications_read_v1_notifications_mark_all_read_post","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/MarkAllReadResponse"}}}}}}},"/v1/branding/{org_id}/{filename}":{"get":{"tags":["branding"],"summary":"Public read proxy for a branding asset.","operationId":"get_branding_asset_v1_branding__org_id___filename__get","parameters":[{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}},{"name":"filename","in":"path","required":true,"schema":{"type":"string","title":"Filename"}}],"responses":{"200":{"description":"Successful Response"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/health":{"get":{"summary":"Health Check","operationId":"health_check_health_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HealthResponse"}}}}}}},"/":{"get":{"summary":"Landing","description":"API service info. The user-facing dashboard lives at idz.holool.dev.","operationId":"landing__get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}}},"components":{"schemas":{"ArtifactCatalogItem":{"properties":{"id":{"type":"string","title":"Id"},"file_name":{"type":"string","title":"File Name"},"mime":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Mime"},"size":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Size"},"href":{"type":"string","title":"Href"},"category":{"type":"string","title":"Category"},"side":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Side"},"field_name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Field Name"}},"type":"object","required":["id","file_name","href","category"],"title":"ArtifactCatalogItem","description":"One artifact reference in the catalog.\n\n`href` points at the same streaming endpoint as `artifacts[].href`\non the main Verification response — same API-key auth, same bytes,\nsame field name (the docs at apps/docs/content/docs/verifications.mdx\nuse `href` too). The catalog adds `category`, `side`, and `field_name`\nso clients can group / label without reverse-engineering filename\npatterns themselves."},"AuditActorOut":{"properties":{"kind":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Kind"},"user_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"User Id"},"org_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Org Id"},"api_key_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Api Key Id"},"name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Name"},"email":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Email"}},"type":"object","title":"AuditActorOut"},"AuditCountsOut":{"properties":{"today":{"type":"integer","title":"Today"},"last_30_days":{"type":"integer","title":"Last 30 Days"},"sensitive_last_30_days":{"type":"integer","title":"Sensitive Last 30 Days"},"retention_years":{"type":"integer","title":"Retention Years"}},"type":"object","required":["today","last_30_days","sensitive_last_30_days","retention_years"],"title":"AuditCountsOut"},"AuditEventOut":{"properties":{"id":{"type":"string","title":"Id"},"timestamp":{"type":"string","format":"date-time","title":"Timestamp"},"category":{"type":"string","title":"Category"},"action":{"type":"string","title":"Action"},"actor":{"$ref":"#/components/schemas/AuditActorOut"},"target":{"$ref":"#/components/schemas/AuditTargetOut"},"request":{"$ref":"#/components/schemas/AuditRequestOut"},"ip_address":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Ip Address"},"user_agent":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"User Agent"},"details":{"anyOf":[{"additionalProperties":true,"type":"object"},{"type":"null"}],"title":"Details"}},"type":"object","required":["id","timestamp","category","action","actor","target","request"],"title":"AuditEventOut"},"AuditFacetsOut":{"properties":{"actors":{"items":{"additionalProperties":true,"type":"object"},"type":"array","title":"Actors"},"actions":{"items":{"type":"string"},"type":"array","title":"Actions"},"categories":{"items":{"type":"string"},"type":"array","title":"Categories"}},"type":"object","required":["actors","actions","categories"],"title":"AuditFacetsOut","description":"Distinct values for the dashboard's filter dropdowns.\n\nScoped to the caller's org + the last 30 days so the response stays\nbounded (the audit table will have millions of rows at 7-year\nretention; distinct-over-all-time is unbounded latency)."},"AuditListOut":{"properties":{"events":{"items":{"$ref":"#/components/schemas/AuditEventOut"},"type":"array","title":"Events"},"next_cursor":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Next Cursor"},"has_more":{"type":"boolean","title":"Has More"}},"type":"object","required":["events","has_more"],"title":"AuditListOut"},"AuditRequestOut":{"properties":{"id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Id"},"method":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Method"},"path":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Path"},"status":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Status"}},"type":"object","title":"AuditRequestOut"},"AuditTargetOut":{"properties":{"type":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Type"},"id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Id"}},"type":"object","title":"AuditTargetOut"},"Body_create_document_verification_v1_verifications_document_post":{"properties":{"id_front":{"type":"string","contentMediaType":"application/octet-stream","title":"Id Front","description":"ID front side image"},"id_back":{"type":"string","contentMediaType":"application/octet-stream","title":"Id Back","description":"ID back side image"},"front_tilt_video":{"anyOf":[{"type":"string","contentMediaType":"application/octet-stream"},{"type":"null"}],"title":"Front Tilt Video","description":"Optional 3–5s MP4 of the front side captured while tilting the card under light. When supplied and the worker has HOLOGRAM_ENABLED=true, the hologram check runs and the response carries a `checks.hologram` block. Absent → existing behavior unchanged (backward compatible)."},"back_tilt_video":{"anyOf":[{"type":"string","contentMediaType":"application/octet-stream"},{"type":"null"}],"title":"Back Tilt Video","description":"Optional 3–5s MP4 of the back side. Reserved for future back-side OVD detection; persisted but not processed yet."},"document_type":{"type":"string","title":"Document Type","description":"Document type. Defaults to algerian_national_id. Allowed values: algerian_national_id, algerian_driving_license.","default":"algerian_national_id"},"nfc_card_reading":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Nfc Card Reading","description":"Optional JSON object containing NFC chip/card reading data. When supplied, machine-readable fields improve final OCR output."}},"type":"object","required":["id_front","id_back"],"title":"Body_create_document_verification_v1_verifications_document_post"},"Body_create_identity_live_verification_v1_verifications_identity_live_post":{"properties":{"id_front":{"type":"string","contentMediaType":"application/octet-stream","title":"Id Front","description":"ID front side image"},"id_back":{"type":"string","contentMediaType":"application/octet-stream","title":"Id Back","description":"ID back side image"},"selfie":{"type":"string","contentMediaType":"application/octet-stream","title":"Selfie","description":"Live selfie"},"video":{"type":"string","contentMediaType":"application/octet-stream","title":"Video","description":"Short video for liveness"},"document_type":{"type":"string","title":"Document Type","description":"Document type. Defaults to algerian_national_id. Allowed values: algerian_national_id, algerian_driving_license.","default":"algerian_national_id"},"nfc_card_reading":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Nfc Card Reading","description":"Optional JSON object containing NFC chip/card reading data. When supplied, machine-readable fields improve final OCR output."}},"type":"object","required":["id_front","id_back","selfie","video"],"title":"Body_create_identity_live_verification_v1_verifications_identity_live_post"},"Body_create_identity_verification_v1_verifications_identity_post":{"properties":{"id_front":{"type":"string","contentMediaType":"application/octet-stream","title":"Id Front","description":"ID front side image"},"id_back":{"type":"string","contentMediaType":"application/octet-stream","title":"Id Back","description":"ID back side image"},"selfie":{"type":"string","contentMediaType":"application/octet-stream","title":"Selfie","description":"Live selfie of the person"},"front_tilt_video":{"anyOf":[{"type":"string","contentMediaType":"application/octet-stream"},{"type":"null"}],"title":"Front Tilt Video","description":"Optional 3–5s MP4 of the front side captured while tilting the card under light. When supplied and the worker has HOLOGRAM_ENABLED=true, the hologram check runs and the response carries a `checks.hologram` block."},"back_tilt_video":{"anyOf":[{"type":"string","contentMediaType":"application/octet-stream"},{"type":"null"}],"title":"Back Tilt Video","description":"Optional 3–5s MP4 of the back side. Reserved for future back-side OVD detection; persisted but not processed yet."},"document_type":{"type":"string","title":"Document Type","description":"Document type. Defaults to algerian_national_id. Allowed values: algerian_national_id, algerian_driving_license.","default":"algerian_national_id"},"nfc_card_reading":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Nfc Card Reading","description":"Optional JSON object containing NFC chip/card reading data. When supplied, machine-readable fields improve final OCR output."}},"type":"object","required":["id_front","id_back","selfie"],"title":"Body_create_identity_verification_v1_verifications_identity_post"},"Body_upload_branding_v1_settings_branding_upload_post":{"properties":{"file":{"type":"string","contentMediaType":"application/octet-stream","title":"File"},"kind":{"type":"string","title":"Kind"}},"type":"object","required":["file","kind"],"title":"Body_upload_branding_v1_settings_branding_upload_post"},"BrandingUploadOut":{"properties":{"kind":{"type":"string","title":"Kind"},"url":{"type":"string","title":"Url"},"content_type":{"type":"string","title":"Content Type"},"size_bytes":{"type":"integer","title":"Size Bytes"}},"type":"object","required":["kind","url","content_type","size_bytes"],"title":"BrandingUploadOut","description":"Response shape for /v1/settings/branding/upload."},"BulkDecisionIn":{"properties":{"session_ids":{"items":{"type":"string"},"type":"array","maxItems":200,"minItems":1,"title":"Session Ids"},"decision":{"type":"string","title":"Decision","description":"approved | rejected | needs_info"},"reason":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Reason"}},"type":"object","required":["session_ids","decision"],"title":"BulkDecisionIn","description":"Body for POST /v1/reviews/bulk-decide.\n\nSame `decision` applied to every session_id. `reason` is shared\nacross the whole batch — operators rarely have per-row reasons\nwhen bulk-deciding from a checkbox selection.\n\n`session_ids` capped at 200 — that's the screen's max page size,\nand going higher would push the request past the proxy's body\nlimits while not buying anything (the operator can't usefully\nreview more than a screen at a time)."},"BulkDecisionOut":{"properties":{"results":{"items":{"$ref":"#/components/schemas/BulkDecisionResult"},"type":"array","title":"Results"},"ok_count":{"type":"integer","title":"Ok Count"},"error_count":{"type":"integer","title":"Error Count"}},"type":"object","required":["results","ok_count","error_count"],"title":"BulkDecisionOut","description":"Aggregated response for bulk-decide.\n\nAlways 200 (not a per-row HTTP status) so the dashboard can render\na partial-success state (\"17 approved, 2 errors\") in one render\nwithout parsing multiple responses."},"BulkDecisionResult":{"properties":{"session_id":{"type":"string","title":"Session Id"},"status":{"type":"string","title":"Status"},"decision_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Decision Id"},"error":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Error"}},"type":"object","required":["session_id","status"],"title":"BulkDecisionResult","description":"One row of bulk-decide's per-session result list.\n\n`status` ∈ \"ok\" | \"error\". On success, `decision_id` is the\nnewly-created ReviewDecision id. On error, `error` carries a\nshort reason (\"not found\", \"invalid\", \"already decided\", …)."},"CheckResult":{"properties":{"passed":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Passed"},"score":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Score"},"failure_reason":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Failure Reason"},"error":{"anyOf":[{"$ref":"#/components/schemas/ErrorPayload"},{"type":"null"}]}},"type":"object","title":"CheckResult","description":"Outcome of a single check (document validity / face match / liveness).\n\n`failure_reason` is kept as a flat string (same value as `error.code`)\nfor backward compatibility with older SDK versions that match on the\nstring. New consumers should prefer `error.code`."},"ClaimNextOut":{"properties":{"session_id":{"type":"string","title":"Session Id"},"internal_id":{"type":"string","title":"Internal Id"},"claimed_at":{"type":"string","format":"date-time","title":"Claimed At"},"claimed_by":{"type":"string","title":"Claimed By"}},"type":"object","required":["session_id","internal_id","claimed_at","claimed_by"],"title":"ClaimNextOut","description":"Response shape for POST /v1/reviews/claim-next.\n\n`session_id` is the public session_id (kyc_…). `internal_id` is the\nUUID FK target — handy for follow-up POST /v1/reviews/{id}/decide\ncalls that take the internal UUID. `claimed_at` is the timestamp\nthe row was updated to mark the claim."},"DecisionIn":{"properties":{"decision":{"type":"string","title":"Decision","description":"approved | rejected | needs_info"},"reason":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Reason"}},"type":"object","required":["decision"],"title":"DecisionIn"},"DecisionOut":{"properties":{"id":{"type":"string","title":"Id"},"session_id":{"type":"string","title":"Session Id"},"decision":{"type":"string","title":"Decision"},"reason":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Reason"},"reviewer_user_id":{"type":"string","title":"Reviewer User Id"},"decided_at":{"type":"string","format":"date-time","title":"Decided At"}},"type":"object","required":["id","session_id","decision","reviewer_user_id","decided_at"],"title":"DecisionOut"},"DeleteOrgIn":{"properties":{"confirm_org_id":{"type":"string","title":"Confirm Org Id"}},"type":"object","required":["confirm_org_id"],"title":"DeleteOrgIn"},"EndpointHealth":{"properties":{"state":{"type":"string","title":"State"},"total":{"type":"integer","title":"Total"},"success":{"type":"integer","title":"Success"},"client_err":{"type":"integer","title":"Client Err"},"server_err":{"type":"integer","title":"Server Err"},"failure_rate_5xx":{"type":"number","title":"Failure Rate 5Xx"}},"type":"object","required":["state","total","success","client_err","server_err","failure_rate_5xx"],"title":"EndpointHealth"},"ErrorPayload":{"properties":{"code":{"type":"string","title":"Code"},"developer_message":{"type":"string","title":"Developer Message"},"user_message":{"type":"string","title":"User Message"},"user_action":{"type":"string","title":"User Action"},"retryable":{"type":"boolean","title":"Retryable"}},"type":"object","required":["code","developer_message","user_message","user_action","retryable"],"title":"ErrorPayload","description":"Structured error surfaced on a failed verification or check.\n\nTwo-tier message system:\n- `developer_message` is for SDK / API consumers (curated detail,\n  no internal traces or library names).\n- `user_message` is for the mobile app's end user (trust-building,\n  actionable copy — never a raw exception). Empty string for\n  developer-only codes (auth / not-found / etc.) that should never\n  reach the user UI.\n\n`code` is the stable lookup key — never renamed. See\n`kyc.errors.ErrorCode` for the catalog. SDKs may localise the\nuser-facing string client-side by switching on `code`."},"FaceMatchArtifacts":{"properties":{"selfie_crop":{"anyOf":[{"$ref":"#/components/schemas/ArtifactCatalogItem"},{"type":"null"}]},"document_face_crop":{"anyOf":[{"$ref":"#/components/schemas/ArtifactCatalogItem"},{"type":"null"}]},"other":{"items":{"$ref":"#/components/schemas/ArtifactCatalogItem"},"type":"array","title":"Other"},"similarity":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Similarity"}},"type":"object","title":"FaceMatchArtifacts","description":"Face-match group: the two crops that fed the comparison + score.\n\nField names mirror the dashboard's review-page consumer:\n  - `selfie_crop`         — the cropped face from the selfie pass.\n  - `document_face_crop`  — the cropped face region on the ID.\n  - `similarity`          — same float as `checks.face_match.score`.\n`other` catches face-match-tagged files that don't fit either\nbucket (legacy comparison composites, etc.) so the catalog stays\nlossless when uncommon filenames show up."},"HTTPValidationError":{"properties":{"detail":{"items":{"$ref":"#/components/schemas/ValidationError"},"type":"array","title":"Detail"}},"type":"object","title":"HTTPValidationError"},"HealthResponse":{"properties":{"status":{"type":"string","title":"Status"}},"type":"object","required":["status"],"title":"HealthResponse"},"HologramCheck":{"properties":{"passed":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Passed"},"score":{"type":"number","title":"Score"},"label":{"type":"string","title":"Label"},"model_version":{"type":"string","title":"Model Version"},"mode":{"type":"string","title":"Mode"},"frame_count":{"type":"integer","title":"Frame Count"},"frame_count_raw":{"type":"integer","title":"Frame Count Raw"},"failure_reason":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Failure Reason"},"error":{"anyOf":[{"$ref":"#/components/schemas/ErrorPayload"},{"type":"null"}]}},"type":"object","required":["score","label","model_version","mode","frame_count","frame_count_raw"],"title":"HologramCheck","description":"Outcome of the optical-hologram (OVD) authenticity check.\n\nDistinct from `CheckResult` because it carries algorithm-specific\nfields the dashboard renders explicitly (model version, frame count,\nraw label).\n\n`passed` is mode-dependent:\n  - mode=\"shadow\": always None — the check is informational, never\n    binding, never influences approval.\n  - mode=\"soft\":   None on valid/indeterminate; False only on\n    non_valid with score below threshold. (One-sided gate: only\n    clear failures block; everything else passes through.)\n  - mode=\"hard\":   True/False from `label == \"valid\"`. Indeterminate\n    still maps to None (we never reject \"we couldn't tell\").\n\n`label` semantics:\n  - \"valid\"         — sequence looks like a real hologram.\n  - \"non_valid\"     — sequence classified as no-hologram.\n  - \"indeterminate\" — too few classifiable frames (e.g. user didn't\n    tilt the card). Triggers a soft \"please retake\" UX, not a fraud\n    signal.\n\n`mode` is whatever HOLOGRAM_MODE was set to when this verification\nran. Surfaced so dashboard reviewers can tell binding vs informational."},"KeyCountsOut":{"properties":{"active_total":{"type":"integer","title":"Active Total"},"revoked_total":{"type":"integer","title":"Revoked Total"}},"type":"object","required":["active_total","revoked_total"],"title":"KeyCountsOut"},"KeyListOut":{"properties":{"keys":{"items":{"$ref":"#/components/schemas/KeyOut"},"type":"array","title":"Keys"},"revoked":{"items":{"$ref":"#/components/schemas/RevokedKeyOut"},"type":"array","title":"Revoked"},"counts":{"$ref":"#/components/schemas/KeyCountsOut"}},"type":"object","required":["keys","revoked","counts"],"title":"KeyListOut"},"KeyOut":{"properties":{"id":{"type":"string","title":"Id"},"name":{"type":"string","title":"Name"},"description":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Description"},"env":{"type":"string","title":"Env"},"suffix":{"type":"string","title":"Suffix"},"permissions":{"items":{"type":"string"},"type":"array","title":"Permissions"},"created_at":{"type":"string","format":"date-time","title":"Created At"},"age_days":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Age Days"},"last_used_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Last Used At"}},"type":"object","required":["id","name","env","suffix","permissions","created_at"],"title":"KeyOut"},"ManualFlagIn":{"properties":{"session_id":{"type":"string","title":"Session Id"},"kind":{"type":"string","maxLength":100,"title":"Kind"},"severity":{"type":"string","title":"Severity","default":"info"},"reason":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Reason"}},"type":"object","required":["session_id","kind"],"title":"ManualFlagIn"},"MarkAllReadResponse":{"properties":{"marked":{"type":"integer","title":"Marked"}},"type":"object","required":["marked"],"title":"MarkAllReadResponse"},"MemberAddIn":{"properties":{"user_id":{"type":"string","maxLength":255,"title":"User Id"},"display_name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Display Name"},"email":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Email"},"auth_provider":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Auth Provider"},"roles":{"items":{"type":"string"},"type":"array","title":"Roles"},"status":{"type":"string","title":"Status","default":"active"}},"type":"object","required":["user_id"],"title":"MemberAddIn"},"MemberListOut":{"properties":{"members":{"items":{"$ref":"#/components/schemas/MemberOut"},"type":"array","title":"Members"}},"type":"object","required":["members"],"title":"MemberListOut"},"MemberOut":{"properties":{"id":{"type":"string","title":"Id"},"org_id":{"type":"string","title":"Org Id"},"user_id":{"type":"string","title":"User Id"},"display_name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Display Name"},"email":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Email"},"auth_provider":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Auth Provider"},"status":{"type":"string","title":"Status"},"roles":{"items":{"type":"string"},"type":"array","title":"Roles"},"invited_at":{"type":"string","format":"date-time","title":"Invited At"},"invited_by":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Invited By"},"last_active_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Last Active At"},"removed_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Removed At"}},"type":"object","required":["id","org_id","user_id","status","roles","invited_at"],"title":"MemberOut"},"NotificationListResponse":{"properties":{"object":{"type":"string","title":"Object","default":"list"},"data":{"items":{"$ref":"#/components/schemas/NotificationOut"},"type":"array","title":"Data"},"has_more":{"type":"boolean","title":"Has More","default":false},"next_cursor":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Next Cursor"}},"type":"object","required":["data"],"title":"NotificationListResponse"},"NotificationOut":{"properties":{"id":{"type":"string","title":"Id"},"type":{"type":"string","title":"Type"},"created_at":{"type":"string","title":"Created At"},"read_at":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Read At"},"subject_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Subject Id"},"title":{"type":"string","title":"Title"},"href":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Href"}},"type":"object","required":["id","type","created_at","title"],"title":"NotificationOut","description":"Single notification on the wire. Mirrors the spec verbatim."},"ResolveFlagIn":{"properties":{"resolution_note":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Resolution Note"}},"type":"object","title":"ResolveFlagIn"},"ReviewCountsOut":{"properties":{"in_queue":{"type":"integer","title":"In Queue"},"over_1h_sla":{"type":"integer","title":"Over 1H Sla"},"resolved_today_approved":{"type":"integer","title":"Resolved Today Approved"},"resolved_today_rejected":{"type":"integer","title":"Resolved Today Rejected"},"resolved_today_needs_info":{"type":"integer","title":"Resolved Today Needs Info"},"p50_wait_seconds_7d":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"P50 Wait Seconds 7D"}},"type":"object","required":["in_queue","over_1h_sla","resolved_today_approved","resolved_today_rejected","resolved_today_needs_info"],"title":"ReviewCountsOut"},"ReviewListOut":{"properties":{"items":{"items":{"$ref":"#/components/schemas/ReviewSessionOut"},"type":"array","title":"Items"},"total":{"type":"integer","title":"Total"},"page":{"type":"integer","title":"Page"},"limit":{"type":"integer","title":"Limit"}},"type":"object","required":["items","total","page","limit"],"title":"ReviewListOut"},"ReviewSessionOut":{"properties":{"session_id":{"type":"string","title":"Session Id"},"public_id":{"type":"string","title":"Public Id"},"status":{"type":"string","title":"Status"},"org_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Org Id"},"created_at":{"type":"string","format":"date-time","title":"Created At"},"updated_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Updated At"},"open_flags":{"type":"integer","title":"Open Flags","default":0},"highest_severity":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Highest Severity"}},"type":"object","required":["session_id","public_id","status","created_at"],"title":"ReviewSessionOut","description":"A session that's awaiting manual review (has open flags, no decision yet)."},"RevokedKeyOut":{"properties":{"id":{"type":"string","title":"Id"},"name":{"type":"string","title":"Name"},"description":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Description"},"env":{"type":"string","title":"Env"},"suffix":{"type":"string","title":"Suffix"},"permissions":{"items":{"type":"string"},"type":"array","title":"Permissions"},"created_at":{"type":"string","format":"date-time","title":"Created At"},"age_days":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Age Days"},"last_used_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Last Used At"},"revoked_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Revoked At"}},"type":"object","required":["id","name","env","suffix","permissions","created_at"],"title":"RevokedKeyOut"},"RiskFlagCountsOut":{"properties":{"open_total":{"type":"integer","title":"Open Total"},"open_info":{"type":"integer","title":"Open Info"},"open_warning":{"type":"integer","title":"Open Warning"},"open_critical":{"type":"integer","title":"Open Critical"},"over_1h_sla":{"type":"integer","title":"Over 1H Sla"},"resolved_today":{"type":"integer","title":"Resolved Today"},"avg_resolution_seconds_p50_7d":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Avg Resolution Seconds P50 7D"}},"type":"object","required":["open_total","open_info","open_warning","open_critical","over_1h_sla","resolved_today"],"title":"RiskFlagCountsOut"},"RiskFlagListOut":{"properties":{"flags":{"items":{"$ref":"#/components/schemas/RiskFlagOut"},"type":"array","title":"Flags"},"next_cursor":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Next Cursor"},"has_more":{"type":"boolean","title":"Has More"}},"type":"object","required":["flags","has_more"],"title":"RiskFlagListOut"},"RiskFlagOut":{"properties":{"id":{"type":"string","title":"Id"},"session_id":{"type":"string","title":"Session Id"},"kind":{"type":"string","title":"Kind"},"source":{"type":"string","title":"Source"},"severity":{"type":"string","title":"Severity"},"reason":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Reason"},"raised_by":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Raised By"},"raised_at":{"type":"string","format":"date-time","title":"Raised At"},"resolved_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Resolved At"},"resolved_by":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Resolved By"},"resolution_note":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Resolution Note"}},"type":"object","required":["id","session_id","kind","source","severity","raised_at"],"title":"RiskFlagOut"},"RoleCatalogOut":{"properties":{"roles":{"additionalProperties":{"items":{"type":"string"},"type":"array"},"type":"object","title":"Roles"}},"type":"object","required":["roles"],"title":"RoleCatalogOut"},"RoleSetIn":{"properties":{"roles":{"items":{"type":"string"},"type":"array","title":"Roles"}},"type":"object","title":"RoleSetIn"},"ScopeCatalogOut":{"properties":{"scopes":{"items":{"type":"string"},"type":"array","title":"Scopes"},"wildcard_grammar":{"type":"string","title":"Wildcard Grammar","default":"Use `<resource>:*` for all verbs on a resource; `*` for super-admin."}},"type":"object","required":["scopes"],"title":"ScopeCatalogOut"},"ScopeListOut":{"properties":{"api_key_id":{"type":"string","title":"Api Key Id"},"scopes":{"items":{"type":"string"},"type":"array","title":"Scopes"}},"type":"object","required":["api_key_id","scopes"],"title":"ScopeListOut"},"ScopeSetIn":{"properties":{"scopes":{"items":{"type":"string"},"type":"array","title":"Scopes"}},"type":"object","title":"ScopeSetIn"},"SearchResponse":{"properties":{"object":{"type":"string","title":"Object","default":"list"},"data":{"items":{"$ref":"#/components/schemas/SearchResult"},"type":"array","title":"Data"},"has_more":{"type":"boolean","title":"Has More","default":false}},"type":"object","required":["data"],"title":"SearchResponse","description":"Top-level response envelope."},"SearchResult":{"properties":{"type":{"type":"string","title":"Type"},"id":{"type":"string","title":"Id"},"matched_field":{"type":"string","title":"Matched Field"},"matched_value":{"type":"string","title":"Matched Value"},"status":{"type":"string","title":"Status"},"created_at":{"type":"string","title":"Created At"},"href":{"type":"string","title":"Href"}},"type":"object","required":["type","id","matched_field","matched_value","status","created_at","href"],"title":"SearchResult","description":"One hit in the search response."},"SettingDefOut":{"properties":{"key":{"type":"string","title":"Key"},"group":{"type":"string","title":"Group"},"sensitive":{"type":"boolean","title":"Sensitive"}},"type":"object","required":["key","group","sensitive"],"title":"SettingDefOut"},"SettingsCatalogOut":{"properties":{"settings":{"items":{"$ref":"#/components/schemas/SettingDefOut"},"type":"array","title":"Settings"},"groups":{"items":{"type":"string"},"type":"array","title":"Groups"}},"type":"object","required":["settings","groups"],"title":"SettingsCatalogOut"},"SettingsMapOut":{"properties":{"org_id":{"type":"string","title":"Org Id"},"group":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Group"},"settings":{"additionalProperties":true,"type":"object","title":"Settings"}},"type":"object","required":["org_id","settings"],"title":"SettingsMapOut","description":"Settings payload returned to the dashboard, grouped by tab.\n\nShape:\n  - `GET /v1/settings`         → `settings = {group: {key: value}}`\n  - `GET /v1/settings/{group}` → `settings = {key: value}`\n                                 (no nesting; the URL fixes the group)\n\nThe flat-keyed storage form (`group.key`) is internal — the wire\nform is nested so the dashboard's per-tab forms can bind to\n`settings.org.name` instead of `settings[\"org.name\"]`."},"SettingsReplaceIn":{"properties":{"settings":{"additionalProperties":true,"type":"object","title":"Settings"}},"type":"object","required":["settings"],"title":"SettingsReplaceIn","description":"PUT body for /v1/settings/{group}.\n\nAccepts either shape:\n  - un-prefixed keys (dashboard convention): `{name: \"Acme\", timezone: \"…\"}`\n  - prefixed keys (legacy / SDK):            `{\"org.name\": \"Acme\", \"org.timezone\": \"…\"}`\n\nThe handler normalises by prefixing the group from the URL if it's\nmissing. A key with the WRONG prefix (e.g. branding.x sent to\n`/v1/settings/org`) returns 400."},"UnreadCountResponse":{"properties":{"count":{"type":"integer","title":"Count"}},"type":"object","required":["count"],"title":"UnreadCountResponse"},"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"},"Verification":{"properties":{"id":{"type":"string","title":"Id"},"public_id":{"type":"string","title":"Public Id"},"mode":{"type":"string","title":"Mode"},"status":{"type":"string","title":"Status"},"review_state":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Review State"},"created_at":{"type":"string","format":"date-time","title":"Created At"},"completed_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Completed At"},"latency_ms":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Latency Ms"},"holder_name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Holder Name"},"source":{"anyOf":[{"$ref":"#/components/schemas/VerificationSource"},{"type":"null"}]},"document":{"anyOf":[{"$ref":"#/components/schemas/VerificationDocument"},{"type":"null"}]},"nfc_card_reading":{"anyOf":[{"additionalProperties":true,"type":"object"},{"type":"null"}],"title":"Nfc Card Reading"},"checks":{"$ref":"#/components/schemas/VerificationChecks"},"artifacts":{"items":{"$ref":"#/components/schemas/VerificationArtifact"},"type":"array","title":"Artifacts"},"metadata":{"anyOf":[{"additionalProperties":true,"type":"object"},{"type":"null"}],"title":"Metadata"},"error":{"anyOf":[{"$ref":"#/components/schemas/ErrorPayload"},{"type":"null"}]}},"type":"object","required":["id","public_id","mode","status","created_at","checks"],"title":"Verification"},"VerificationArtifact":{"properties":{"id":{"type":"string","title":"Id"},"type":{"type":"string","title":"Type"},"mime":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Mime"},"size":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Size"},"href":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Href"}},"type":"object","required":["id","type"],"title":"VerificationArtifact"},"VerificationArtifactsCatalog":{"properties":{"uploads":{"items":{"$ref":"#/components/schemas/ArtifactCatalogItem"},"type":"array","title":"Uploads"},"detections":{"items":{"$ref":"#/components/schemas/ArtifactCatalogItem"},"type":"array","title":"Detections"},"crops":{"items":{"$ref":"#/components/schemas/ArtifactCatalogItem"},"type":"array","title":"Crops"},"face_match":{"$ref":"#/components/schemas/FaceMatchArtifacts"},"liveness":{"items":{"$ref":"#/components/schemas/ArtifactCatalogItem"},"type":"array","title":"Liveness"},"other":{"items":{"$ref":"#/components/schemas/ArtifactCatalogItem"},"type":"array","title":"Other"}},"type":"object","title":"VerificationArtifactsCatalog","description":"Artifacts grouped by purpose for the result-screen Artifacts tab.\n\n`uploads` are the original front/back/selfie/video the caller submitted.\n`detections` are full-image visualisations with YOLO bounding boxes.\n`crops` are per-field ROI crops that fed the OCR pipeline.\n`face_match` carries the selfie/id-photo crops + similarity score.\n`liveness` are sampled frames from the liveness video pass.\n`other` catches anything that doesn't classify cleanly — debug images\nfrom old runs, processor-emitted outputs without recognisable prefixes."},"VerificationAuditEntry":{"properties":{"timestamp":{"type":"string","format":"date-time","title":"Timestamp"},"action":{"type":"string","title":"Action"},"actor_kind":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Actor Kind"},"details":{"anyOf":[{"additionalProperties":true,"type":"object"},{"type":"null"}],"title":"Details"}},"type":"object","required":["timestamp","action"],"title":"VerificationAuditEntry","description":"One row from a verification's audit trail.\n\n`actor_kind` is exposed (api / client / system) so the consumer can\ndistinguish their own POST events from platform admin actions on the\nsession. Internal user_ids and api_key_ids are NOT exposed."},"VerificationChecks":{"properties":{"document":{"anyOf":[{"$ref":"#/components/schemas/CheckResult"},{"type":"null"}]},"face_match":{"anyOf":[{"$ref":"#/components/schemas/CheckResult"},{"type":"null"}]},"liveness":{"anyOf":[{"$ref":"#/components/schemas/CheckResult"},{"type":"null"}]},"hologram":{"anyOf":[{"$ref":"#/components/schemas/HologramCheck"},{"type":"null"}]}},"type":"object","title":"VerificationChecks","description":"Per-check results. Null entries indicate the check wasn't evaluated\nin this mode (caller can use this to detect missing assurances)."},"VerificationDocument":{"properties":{"type":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Type"},"country":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Country"},"fields":{"anyOf":[{"additionalProperties":true,"type":"object"},{"type":"null"}],"title":"Fields"}},"type":"object","title":"VerificationDocument"},"VerificationList":{"properties":{"object":{"type":"string","title":"Object","default":"list"},"data":{"items":{"$ref":"#/components/schemas/Verification"},"type":"array","title":"Data"},"verifications":{"items":{"$ref":"#/components/schemas/Verification"},"type":"array","title":"Verifications"},"has_more":{"type":"boolean","title":"Has More","default":false},"next_cursor":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Next Cursor"}},"type":"object","title":"VerificationList","description":"Cursor-paginated list of verifications.\n\nTwo equivalent fields hold the rows: `data` (SDK-style, matches the\nStripe / Linear list envelope) and `verifications` (dashboard\nconvention, matches `/v1/risk-flags` and friends). Both reference\nthe same list — pick whichever your client prefers. SDK consumers\ntypically read `data`; the dashboard reads `verifications`.\n\n`next_cursor` is opaque — pass it back as the `cursor` query param\nto fetch the next page. None when no more rows exist."},"VerificationSource":{"properties":{"kind":{"type":"string","title":"Kind"},"label":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Label"},"key_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Key Id"}},"type":"object","required":["kind"],"title":"VerificationSource","description":"Where this verification was submitted from — surfaces in the\ndashboard's Verifications table.\n\n`kind` ∈ \"api\" | \"client\" | \"system\"\n`key_id` — internal api_key_id when kind=\"api\"; None otherwise\n`label` — human-readable identifier (\"API key … 8c2e\" / \"Dashboard\")"},"WebhookCountsOut":{"properties":{"endpoints_total":{"type":"integer","title":"Endpoints Total"},"endpoints_enabled":{"type":"integer","title":"Endpoints Enabled"},"endpoints_paused":{"type":"integer","title":"Endpoints Paused"},"endpoints_degraded":{"type":"integer","title":"Endpoints Degraded"},"deliveries_24h_total":{"type":"integer","title":"Deliveries 24H Total"},"deliveries_24h_success":{"type":"integer","title":"Deliveries 24H Success"},"deliveries_24h_failed":{"type":"integer","title":"Deliveries 24H Failed"}},"type":"object","required":["endpoints_total","endpoints_enabled","endpoints_paused","endpoints_degraded","deliveries_24h_total","deliveries_24h_success","deliveries_24h_failed"],"title":"WebhookCountsOut"},"WebhookDeliveryListOut":{"properties":{"deliveries":{"items":{"$ref":"#/components/schemas/WebhookDeliveryOut"},"type":"array","title":"Deliveries"},"next_cursor":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Next Cursor"},"has_more":{"type":"boolean","title":"Has More"}},"type":"object","required":["deliveries","has_more"],"title":"WebhookDeliveryListOut"},"WebhookDeliveryOut":{"properties":{"id":{"type":"string","title":"Id"},"endpoint_id":{"type":"string","title":"Endpoint Id"},"endpoint_url":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Endpoint Url"},"event_type":{"type":"string","title":"Event Type"},"event_id":{"type":"string","title":"Event Id"},"status":{"type":"string","title":"Status"},"attempt":{"type":"integer","title":"Attempt"},"response_status":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Response Status"},"response_body":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Response Body"},"error":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Error"},"latency_ms":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Latency Ms"},"created_at":{"type":"string","format":"date-time","title":"Created At"},"last_attempt_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Last Attempt At"},"next_retry_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Next Retry At"}},"type":"object","required":["id","endpoint_id","event_type","event_id","status","attempt","created_at"],"title":"WebhookDeliveryOut"},"WebhookDeliveryResponse":{"properties":{"id":{"type":"string","title":"Id"},"endpoint_id":{"type":"string","title":"Endpoint Id"},"event_type":{"type":"string","title":"Event Type"},"event_id":{"type":"string","title":"Event Id"},"attempt":{"type":"integer","title":"Attempt"},"status":{"type":"string","title":"Status"},"response_status":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Response Status"},"error":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Error"},"created_at":{"type":"string","format":"date-time","title":"Created At"},"last_attempt_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Last Attempt At"},"next_retry_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Next Retry At"}},"type":"object","required":["id","endpoint_id","event_type","event_id","attempt","status","created_at"],"title":"WebhookDeliveryResponse"},"WebhookEndpointCreate":{"properties":{"url":{"type":"string","minLength":1,"format":"uri","title":"Url"},"events":{"items":{"type":"string"},"type":"array","minItems":1,"title":"Events"},"description":{"anyOf":[{"type":"string","maxLength":2048},{"type":"null"}],"title":"Description"}},"type":"object","required":["url"],"title":"WebhookEndpointCreate"},"WebhookEndpointListOut":{"properties":{"endpoints":{"items":{"$ref":"#/components/schemas/WebhookEndpointOut"},"type":"array","title":"Endpoints"}},"type":"object","required":["endpoints"],"title":"WebhookEndpointListOut"},"WebhookEndpointOut":{"properties":{"id":{"type":"string","title":"Id"},"org_id":{"type":"string","title":"Org Id"},"url":{"type":"string","title":"Url"},"description":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Description"},"events":{"items":{"type":"string"},"type":"array","title":"Events"},"enabled":{"type":"boolean","title":"Enabled"},"created_at":{"type":"string","format":"date-time","title":"Created At"},"updated_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Updated At"},"last_delivery_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Last Delivery At"},"health":{"$ref":"#/components/schemas/EndpointHealth"}},"type":"object","required":["id","org_id","url","events","enabled","created_at","health"],"title":"WebhookEndpointOut"},"WebhookEndpointResponse":{"properties":{"id":{"type":"string","title":"Id"},"url":{"type":"string","title":"Url"},"description":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Description"},"events":{"items":{"type":"string"},"type":"array","title":"Events"},"enabled":{"type":"boolean","title":"Enabled"},"created_at":{"type":"string","format":"date-time","title":"Created At"},"updated_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Updated At"},"last_delivery_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Last Delivery At"}},"type":"object","required":["id","url","events","enabled","created_at"],"title":"WebhookEndpointResponse","description":"Response shape that does NOT include signing_secret."},"WebhookEndpointUpdate":{"properties":{"url":{"anyOf":[{"type":"string","minLength":1,"format":"uri"},{"type":"null"}],"title":"Url"},"events":{"anyOf":[{"items":{"type":"string"},"type":"array"},{"type":"null"}],"title":"Events"},"description":{"anyOf":[{"type":"string","maxLength":2048},{"type":"null"}],"title":"Description"},"enabled":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Enabled"}},"type":"object","title":"WebhookEndpointUpdate"},"WebhookEndpointWithSecret":{"properties":{"id":{"type":"string","title":"Id"},"url":{"type":"string","title":"Url"},"description":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Description"},"events":{"items":{"type":"string"},"type":"array","title":"Events"},"enabled":{"type":"boolean","title":"Enabled"},"created_at":{"type":"string","format":"date-time","title":"Created At"},"updated_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Updated At"},"last_delivery_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Last Delivery At"},"signing_secret":{"type":"string","title":"Signing Secret"}},"type":"object","required":["id","url","events","enabled","created_at","signing_secret"],"title":"WebhookEndpointWithSecret","description":"Returned ONCE, on create or rotate_secret. Persist it on your end —\nwe don't show it again."}}}}