{"openapi":"3.1.0","info":{"title":"Venezuela Reunificación — FR API","description":"API de coincidencias faciales ASISTIVAS para reunificación tras el terremoto. Requiere API key (header `X-API-Key`). Es asistiva: las coincidencias siempre exigen verificación humana, nunca identifican de forma definitiva. Datos y fotos provienen de la API de Azure.","version":"1.0.0"},"paths":{"/health":{"get":{"summary":"Health","description":"Salud del servicio. indexed=0 si no hay índice cargado.","operationId":"health_health_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"additionalProperties":true,"type":"object","title":"Response Health Health Get"}}}}}}},"/stats":{"get":{"summary":"Stats","description":"Conteos del índice vectorial (Qdrant).","operationId":"stats_stats_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"additionalProperties":true,"type":"object","title":"Response Stats Stats Get"}}}}}}},"/search":{"post":{"summary":"Search","description":"Busca posibles coincidencias para TODAS las caras de la imagen.\n\nAcepta la imagen por CUALQUIER vía: `file` (multipart), `image_url` (URL\nhttp/https) o `image_url` con un data-URI base64. Cualquier formato/extensión\n(JPG/PNG/WebP/HEIC…). Multi-rostro + piso min_score (default 0.51).","operationId":"search_search_post","parameters":[{"name":"min_score","in":"query","required":false,"schema":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Min Score"}}],"requestBody":{"content":{"multipart/form-data":{"schema":{"$ref":"#/components/schemas/Body_search_search_post"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/groups":{"get":{"summary":"Listar Grupos","description":"Proxy a la API EN VIVO: lista de grupos para navegar en el Modo B.","operationId":"listar_grupos_groups_get","parameters":[{"name":"limit","in":"query","required":false,"schema":{"type":"integer","default":20,"title":"Limit"}},{"name":"offset","in":"query","required":false,"schema":{"type":"integer","default":0,"title":"Offset"}},{"name":"search","in":"query","required":false,"schema":{"type":"string","default":"","title":"Search"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/groups/{group_id}/analyze":{"get":{"summary":"Analizar Grupo","description":"Trae el grupo EN VIVO, cruza las fotos de sus miembros con reconocimiento\nfacial y CLASIFICA cada duplicado propuesto: score textual (de la API) +\nscore FACIAL + veredicto + recomendación de limpieza. El humano valida.","operationId":"analizar_grupo_groups__group_id__analyze_get","parameters":[{"name":"group_id","in":"path","required":true,"schema":{"type":"string","title":"Group Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/groups/{group_id}/records":{"get":{"summary":"Grupo Records","description":"RÁPIDO: trae solo los registros del grupo (con sus fotos) de la API EN\nVIVO, sin correr reconocimiento facial. La UI los muestra al instante y\nsuperpone la animación de 'comparando' mientras /analyze hace el cruce.","operationId":"grupo_records_groups__group_id__records_get","parameters":[{"name":"group_id","in":"path","required":true,"schema":{"type":"string","title":"Group Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/groups/{group_id}/cluster":{"get":{"summary":"Cluster Grupo","description":"Agrupa por ROSTRO todas las imágenes del grupo a la vez. Detecta TODAS\nlas caras de cada foto (aunque haya varias personas), las agrupa en\n\"personas\" (caras mutuamente parecidas) y devuelve cada persona con los\nregistros donde aparece. Las imágenes SIN rostro se separan para\nverificación manual. Solo se comparan por IA las que tienen rostro.","operationId":"cluster_grupo_groups__group_id__cluster_get","parameters":[{"name":"group_id","in":"path","required":true,"schema":{"type":"string","title":"Group Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/check-duplicate":{"post":{"tags":["v1"],"summary":"V1 Check Duplicate","description":"REGISTRO anti-duplicado: recibe la foto que alguien va a registrar y\nresponde si esa persona probablemente YA está reportada. Acepta la imagen por\nCUALQUIER vía: `file` (multipart), `image_url` (URL http/https) o data-URI\nbase64; cualquier formato (JPG/PNG/WebP/HEIC). Multi-rostro: marca cada cara\n(verde=coincide / amarillo=no). Duplicado si hay match con score >= min_score (0.51).","operationId":"v1_check_duplicate_v1_check_duplicate_post","parameters":[{"name":"min_score","in":"query","required":false,"schema":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Min Score"}},{"name":"X-API-Key","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Api-Key"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"requestBody":{"content":{"multipart/form-data":{"schema":{"$ref":"#/components/schemas/Body_v1_check_duplicate_v1_check_duplicate_post"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/search":{"post":{"tags":["v1"],"summary":"V1 Search","description":"Búsqueda 1:N multi-rostro. Acepta `file`, `image_url` (URL http/https) o\ndata-URI base64; cualquier formato. Devuelve `faces` con bbox+matched.","operationId":"v1_search_v1_search_post","parameters":[{"name":"min_score","in":"query","required":false,"schema":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Min Score"}},{"name":"X-API-Key","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Api-Key"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"requestBody":{"content":{"multipart/form-data":{"schema":{"$ref":"#/components/schemas/Body_v1_search_v1_search_post"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/whoami":{"get":{"tags":["v1"],"summary":"V1 Whoami","description":"AUTODIAGNÓSTICO en una llamada. Si responde 200, tu API key es válida.\nTe dice tu `source` por defecto (= etiqueta de tu key), cuántos registros\ntienes indexados bajo TU fuente, y el piso real (`min_score`). Si\n`indexed_my_source` es 0, debes hacer backfill con POST /v1/index (el FR-API\nNO consulta tu base de datos: solo compara contra lo que indexes).","operationId":"v1_whoami_v1_whoami_get","parameters":[{"name":"X-API-Key","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Api-Key"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/groups":{"get":{"tags":["v1"],"summary":"V1 Groups","description":"Lista de grupos (proxy a la API de Azure).","operationId":"v1_groups_v1_groups_get","parameters":[{"name":"limit","in":"query","required":false,"schema":{"type":"integer","default":20,"title":"Limit"}},{"name":"offset","in":"query","required":false,"schema":{"type":"integer","default":0,"title":"Offset"}},{"name":"q","in":"query","required":false,"schema":{"type":"string","default":"","title":"Q"}},{"name":"X-API-Key","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Api-Key"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/groups/{group_id}/cluster":{"get":{"tags":["v1"],"summary":"V1 Cluster","description":"Agrupa por rostro todas las imágenes del grupo (depuración asistida).","operationId":"v1_cluster_v1_groups__group_id__cluster_get","parameters":[{"name":"group_id","in":"path","required":true,"schema":{"type":"string","title":"Group Id"}},{"name":"X-API-Key","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Api-Key"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/index":{"post":{"tags":["v1"],"summary":"V1 Index","description":"Indexa UN registro de la plataforma (su foto + datos) para que sea\nbuscable por rostro. Idempotente por (source, external_id). Tolerante: si no\nhay rostro, responde indexed=false sin error (no rompe el registro).","operationId":"v1_index_v1_index_post","parameters":[{"name":"X-API-Key","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Api-Key"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"requestBody":{"required":true,"content":{"multipart/form-data":{"schema":{"$ref":"#/components/schemas/Body_v1_index_v1_index_post"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/index/commit":{"post":{"tags":["v1"],"summary":"V1 Index Commit","description":"No-op de compatibilidad: Qdrant persiste automáticamente.","operationId":"v1_index_commit_v1_index_commit_post","parameters":[{"name":"X-API-Key","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Api-Key"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/duplicates":{"get":{"tags":["v1"],"summary":"V1 Duplicates","description":"MODO 3 — cruzar TU propia base por rostro y listar posibles duplicados.\nPor cada registro de tu `source` busca caras muy parecidas del mismo source\ny devuelve los pares (para que confirmes/elimines). Asistivo.","operationId":"v1_duplicates_v1_duplicates_get","parameters":[{"name":"source","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Source"}},{"name":"min_score","in":"query","required":false,"schema":{"type":"number","default":0.6,"title":"Min Score"}},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","default":200,"title":"Limit"}},{"name":"X-API-Key","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Api-Key"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/reconcile":{"get":{"tags":["v1"],"summary":"V1 Reconcile","description":"Concilia caras entre fuentes DISTINTAS. Recorre puntos del índice (todas\nlas fuentes, o solo las de `sources=\"A,B\"`), agrupa por rostro uniendo\nvecinos con coseno >= min_score, y devuelve los grupos que abarcan >=2\nfuentes distintas, ordenados por score desc. Lo primordial: las imágenes de\ncada base. Acota el coste con `limit` (máx 800).","operationId":"v1_reconcile_v1_reconcile_get","parameters":[{"name":"min_score","in":"query","required":false,"schema":{"type":"number","default":0.5,"title":"Min Score"}},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","default":300,"title":"Limit"}},{"name":"sources","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Sources"}},{"name":"X-API-Key","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Api-Key"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}}},"components":{"schemas":{"Body_search_search_post":{"properties":{"file":{"anyOf":[{"type":"string","contentMediaType":"application/octet-stream"},{"type":"null"}],"title":"File"},"image_url":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Image Url"}},"type":"object","title":"Body_search_search_post"},"Body_v1_check_duplicate_v1_check_duplicate_post":{"properties":{"file":{"anyOf":[{"type":"string","contentMediaType":"application/octet-stream"},{"type":"null"}],"title":"File"},"image_url":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Image Url"}},"type":"object","title":"Body_v1_check_duplicate_v1_check_duplicate_post"},"Body_v1_index_v1_index_post":{"properties":{"external_id":{"type":"string","title":"External Id"},"image_url":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Image Url"},"person_name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Person Name"},"age":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Age"},"last_seen_location":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Last Seen Location"},"contact_phone":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Contact Phone"},"source":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Source"},"persist":{"type":"string","title":"Persist","default":"true"},"file":{"anyOf":[{"type":"string","contentMediaType":"application/octet-stream"},{"type":"null"}],"title":"File"}},"type":"object","required":["external_id"],"title":"Body_v1_index_v1_index_post"},"Body_v1_search_v1_search_post":{"properties":{"file":{"anyOf":[{"type":"string","contentMediaType":"application/octet-stream"},{"type":"null"}],"title":"File"},"image_url":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Image Url"}},"type":"object","title":"Body_v1_search_v1_search_post"},"HTTPValidationError":{"properties":{"detail":{"items":{"$ref":"#/components/schemas/ValidationError"},"type":"array","title":"Detail"}},"type":"object","title":"HTTPValidationError"},"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"}},"securitySchemes":{"ApiKeyAuth":{"type":"apiKey","in":"header","name":"X-API-Key"}}}}