{"openapi":"3.1.0","info":{"title":"Faro API","description":"## Developer access\n\nThis reference is public; business data and key management require authorization.\nAn owner or administrator signs in to [Faro Settings](https://app.koplai.com/settings)\nand opens **Developer API** to create, list or revoke keys for that business.\nAccount access comes from the existing owner onboarding link or a team administrator,\nnot a Meta app developer role. There is no public API-key signup endpoint.\n\n### First request (read-only)\n\nCreate a key with `conversations:read`. Store it in your server secret manager as\n`FARO_API_KEY`; obtain `TENANT_ID` from Settings. Never embed a key in browser/mobile code.\n\n```bash\ncurl --fail-with-body \"https://wa.koplai.com/api/v1/tenants/$TENANT_ID/conversations\" \\\n  --header \"Authorization: Bearer $FARO_API_KEY\"\n```\n\nThe key's tenant is enforced by the server. It acts as AGENT, not administrator.\nEach allowed operation carries `x-faro-scope` in this specification.\nSupported scopes: `conversations:read`, `conversations:write`, `contacts:read`,\n`templates:send`, `agent:tools`. `webhooks:manage` is reserved; outbound webhook\nsubscriptions are not available. Key management requires a session cookie, not Bearer auth.\nThe key is returned only once on creation; lists contain metadata, never the secret.\n\n### Real API, not a sandbox\n\nManual mode does not block outbound API sends. Write, template and assistant approval\noperations can contact real customers. Keep human approval in your integration and use\nread-only calls until a controlled delivery test is explicitly approved. Sending also depends\non channel connection, contracted capabilities and WhatsApp's 24-hour/template rules.\n\n### Errors and limits\n\n`401`: missing/invalid/revoked credentials. `403`: wrong tenant, missing scope or role;\nmissing scopes include `detail.required_scope`. `400`/`422`: invalid input.\n`429`: wait for `Retry-After`. Limit: 600 requests/minute/key, replenished at 10/s,\nper process. Do not automatically retry message sends after ambiguous failures.\n","version":"0.1.0"},"paths":{"/api/v1/auth/login":{"post":{"tags":["sesión"],"summary":"Login","operationId":"login_api_v1_auth_login_post","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/api/v1/auth/logout":{"post":{"tags":["sesión"],"summary":"Logout","operationId":"logout_api_v1_auth_logout_post","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/api/v1/me":{"get":{"tags":["sesión"],"summary":"Me","operationId":"me_api_v1_me_get","security":[{"ClaveAPI":[]}],"parameters":[{"name":"koplai_session","in":"cookie","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Koplai Session"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/tenants":{"get":{"tags":["tenants"],"summary":"Listar Tenants","operationId":"listar_tenants_api_v1_tenants_get","security":[{"ClaveAPI":[]}],"parameters":[{"name":"koplai_session","in":"cookie","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Koplai Session"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/tenants/{tenant_id}":{"patch":{"tags":["tenants"],"summary":"Editar Negocio","description":"Los datos del negocio que su dueño puede cambiar.\n\nSolo el nombre y la zona horaria. NO el modo de la IA —eso va por su propia pantalla, con\nsu comprobación de lo contratado— ni el estado, ni nada que roce la credencial.\n\nLa zona horaria importa más de lo que parece: de ella dependen los huecos de la agenda y\nla decisión de si el asistente atiende porque estáis cerrados. Con ella mal, un negocio de\nCanarias tendría el asistente desfasado una hora sin que nada avisara.","operationId":"editar_negocio_api_v1_tenants__tenant_id__patch","security":[{"ClaveAPI":[]}],"parameters":[{"name":"tenant_id","in":"path","required":true,"schema":{"type":"string","title":"Tenant Id"}},{"name":"koplai_session","in":"cookie","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Koplai Session"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/tenants/{tenant_id}/health":{"get":{"tags":["tenants"],"summary":"Salud Tenant","description":"Tres salud distintas, porque tres fallos distintos exigen tres acciones distintas:\nla app, el canal con Meta y la credencial.","operationId":"salud_tenant_api_v1_tenants__tenant_id__health_get","security":[{"ClaveAPI":[]}],"parameters":[{"name":"tenant_id","in":"path","required":true,"schema":{"type":"string","title":"Tenant Id"}},{"name":"koplai_session","in":"cookie","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Koplai Session"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/tenants/{tenant_id}/config":{"patch":{"tags":["tenants"],"summary":"Configurar Tenant","operationId":"configurar_tenant_api_v1_tenants__tenant_id__config_patch","security":[{"ClaveAPI":[]}],"parameters":[{"name":"tenant_id","in":"path","required":true,"schema":{"type":"string","title":"Tenant Id"}},{"name":"koplai_session","in":"cookie","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Koplai Session"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/tenants/{tenant_id}/conversations":{"get":{"tags":["conversaciones"],"summary":"Listar Conversaciones","operationId":"listar_conversaciones_api_v1_tenants__tenant_id__conversations_get","security":[{"ClaveAPI":[]}],"parameters":[{"name":"tenant_id","in":"path","required":true,"schema":{"type":"string","title":"Tenant Id"}},{"name":"q","in":"query","required":false,"schema":{"type":"string","default":"","title":"Q"}},{"name":"status","in":"query","required":false,"schema":{"type":"string","default":"","title":"Status"}},{"name":"assigned","in":"query","required":false,"schema":{"type":"string","default":"","title":"Assigned"}},{"name":"channel","in":"query","required":false,"schema":{"type":"string","default":"","title":"Channel"}},{"name":"koplai_session","in":"cookie","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Koplai Session"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"x-faro-scope":"conversations:read"}},"/api/v1/tenants/{tenant_id}/conversations/{conversation_id}":{"get":{"tags":["conversaciones"],"summary":"Ver Conversacion","operationId":"ver_conversacion_api_v1_tenants__tenant_id__conversations__conversation_id__get","security":[{"ClaveAPI":[]}],"parameters":[{"name":"tenant_id","in":"path","required":true,"schema":{"type":"string","title":"Tenant Id"}},{"name":"conversation_id","in":"path","required":true,"schema":{"type":"string","title":"Conversation Id"}},{"name":"koplai_session","in":"cookie","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Koplai Session"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"x-faro-scope":"conversations:read"}},"/api/v1/tenants/{tenant_id}/conversations/{conversation_id}/read":{"post":{"tags":["conversaciones"],"summary":"Marcar Leida","operationId":"marcar_leida_api_v1_tenants__tenant_id__conversations__conversation_id__read_post","security":[{"ClaveAPI":[]}],"parameters":[{"name":"tenant_id","in":"path","required":true,"schema":{"type":"string","title":"Tenant Id"}},{"name":"conversation_id","in":"path","required":true,"schema":{"type":"string","title":"Conversation Id"}},{"name":"koplai_session","in":"cookie","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Koplai Session"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"x-faro-scope":"conversations:write"}},"/api/v1/tenants/{tenant_id}/conversations/{conversation_id}/messages":{"post":{"tags":["sesión"],"summary":"Enviar","description":"Envío manual desde el Inbox. El frontend nunca habla con Meta; habla con esto.","operationId":"enviar_api_v1_tenants__tenant_id__conversations__conversation_id__messages_post","security":[{"ClaveAPI":[]}],"parameters":[{"name":"tenant_id","in":"path","required":true,"schema":{"type":"string","title":"Tenant Id"}},{"name":"conversation_id","in":"path","required":true,"schema":{"type":"string","title":"Conversation Id"}},{"name":"koplai_session","in":"cookie","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Koplai Session"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"x-faro-scope":"conversations:write"}},"/api/v1/tenants/{tenant_id}/conversations/{conversation_id}/suggest":{"post":{"tags":["conversaciones"],"summary":"Sugerir","description":"Ejecuta el agente del negocio sobre la conversación real.\n\nCopiloto y piloto automático usan ESTE mismo runtime. Lo único que cambia es qué se hace\ndespués con lo que propone: aquí se guarda para que una persona lo revise.","operationId":"sugerir_api_v1_tenants__tenant_id__conversations__conversation_id__suggest_post","security":[{"ClaveAPI":[]}],"parameters":[{"name":"tenant_id","in":"path","required":true,"schema":{"type":"string","title":"Tenant Id"}},{"name":"conversation_id","in":"path","required":true,"schema":{"type":"string","title":"Conversation Id"}},{"name":"koplai_session","in":"cookie","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Koplai Session"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"x-faro-scope":"agent:tools"}},"/api/v1/tenants/{tenant_id}/suggestions/{suggestion_id}/approve":{"post":{"tags":["conversaciones"],"summary":"Aprobar Sugerencia","description":"Aprueba (con o sin edición) y envía. El texto que se manda es el que el humano confirma,\nno el que produjo la IA: por eso se acepta `body` y se registra la procedencia como aprobada.","operationId":"aprobar_sugerencia_api_v1_tenants__tenant_id__suggestions__suggestion_id__approve_post","security":[{"ClaveAPI":[]}],"parameters":[{"name":"tenant_id","in":"path","required":true,"schema":{"type":"string","title":"Tenant Id"}},{"name":"suggestion_id","in":"path","required":true,"schema":{"type":"string","title":"Suggestion Id"}},{"name":"koplai_session","in":"cookie","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Koplai Session"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"x-faro-scope":"agent:tools"}},"/api/v1/tenants/{tenant_id}/suggestions/{suggestion_id}/discard":{"post":{"tags":["conversaciones"],"summary":"Descartar Sugerencia","operationId":"descartar_sugerencia_api_v1_tenants__tenant_id__suggestions__suggestion_id__discard_post","security":[{"ClaveAPI":[]}],"parameters":[{"name":"tenant_id","in":"path","required":true,"schema":{"type":"string","title":"Tenant Id"}},{"name":"suggestion_id","in":"path","required":true,"schema":{"type":"string","title":"Suggestion Id"}},{"name":"koplai_session","in":"cookie","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Koplai Session"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"x-faro-scope":"agent:tools"}},"/api/v1/tenants/{tenant_id}/audit":{"get":{"tags":["tenants"],"summary":"Auditoria","operationId":"auditoria_api_v1_tenants__tenant_id__audit_get","security":[{"ClaveAPI":[]}],"parameters":[{"name":"tenant_id","in":"path","required":true,"schema":{"type":"string","title":"Tenant Id"}},{"name":"koplai_session","in":"cookie","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Koplai Session"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/tenants/{tenant_id}/conversations/{conversation_id}/assign":{"post":{"tags":["conversaciones"],"summary":"Asignar","description":"Toma la conversación. Si ya la lleva otra persona, devuelve 409 con quién.\n\nEsto es lo que evita que dos empleados de la misma oficina contesten a la vez al mismo\ncliente. No es un bloqueo duro —nadie queda encerrado— sino propiedad visible: el que\nllega segundo ve quién la tiene antes de escribir.","operationId":"asignar_api_v1_tenants__tenant_id__conversations__conversation_id__assign_post","security":[{"ClaveAPI":[]}],"parameters":[{"name":"tenant_id","in":"path","required":true,"schema":{"type":"string","title":"Tenant Id"}},{"name":"conversation_id","in":"path","required":true,"schema":{"type":"string","title":"Conversation Id"}},{"name":"koplai_session","in":"cookie","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Koplai Session"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"x-faro-scope":"conversations:write"}},"/api/v1/tenants/{tenant_id}/conversations/{conversation_id}/unassign":{"post":{"tags":["conversaciones"],"summary":"Liberar","description":"Libera la conversación. Puede hacerlo quien la lleva, o un administrador —si no, una\nconversación quedaría bloqueada cuando alguien se va de vacaciones.","operationId":"liberar_api_v1_tenants__tenant_id__conversations__conversation_id__unassign_post","security":[{"ClaveAPI":[]}],"parameters":[{"name":"tenant_id","in":"path","required":true,"schema":{"type":"string","title":"Tenant Id"}},{"name":"conversation_id","in":"path","required":true,"schema":{"type":"string","title":"Conversation Id"}},{"name":"koplai_session","in":"cookie","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Koplai Session"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"x-faro-scope":"conversations:write"}},"/api/v1/tenants/{tenant_id}/conversations/{conversation_id}/status":{"post":{"tags":["conversaciones"],"summary":"Cambiar Estado","operationId":"cambiar_estado_api_v1_tenants__tenant_id__conversations__conversation_id__status_post","security":[{"ClaveAPI":[]}],"parameters":[{"name":"tenant_id","in":"path","required":true,"schema":{"type":"string","title":"Tenant Id"}},{"name":"conversation_id","in":"path","required":true,"schema":{"type":"string","title":"Conversation Id"}},{"name":"koplai_session","in":"cookie","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Koplai Session"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"x-faro-scope":"conversations:write"}},"/api/v1/tenants/{tenant_id}/team":{"get":{"tags":["equipo"],"summary":"Equipo","operationId":"equipo_api_v1_tenants__tenant_id__team_get","security":[{"ClaveAPI":[]}],"parameters":[{"name":"tenant_id","in":"path","required":true,"schema":{"type":"string","title":"Tenant Id"}},{"name":"koplai_session","in":"cookie","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Koplai Session"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"post":{"tags":["equipo"],"summary":"Anadir Miembro","description":"Da de alta a alguien del equipo del negocio.\n\nNadie puede conceder un rol por encima del suyo: un ADMIN no puede crear un OWNER. Sin\nesa regla, cualquier administrador podría promocionarse a sí mismo por la puerta de atrás.","operationId":"anadir_miembro_api_v1_tenants__tenant_id__team_post","security":[{"ClaveAPI":[]}],"parameters":[{"name":"tenant_id","in":"path","required":true,"schema":{"type":"string","title":"Tenant Id"}},{"name":"koplai_session","in":"cookie","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Koplai Session"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/tenants/{tenant_id}/team/{user_id}":{"delete":{"tags":["equipo"],"summary":"Quitar Miembro","operationId":"quitar_miembro_api_v1_tenants__tenant_id__team__user_id__delete","security":[{"ClaveAPI":[]}],"parameters":[{"name":"tenant_id","in":"path","required":true,"schema":{"type":"string","title":"Tenant Id"}},{"name":"user_id","in":"path","required":true,"schema":{"type":"string","title":"User Id"}},{"name":"koplai_session","in":"cookie","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Koplai Session"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/me/password":{"post":{"tags":["sesión"],"summary":"Cambiar Mi Contrasena","description":"Cambiar la propia contraseña.\n\nSe exige la actual aunque ya haya sesión: si alguien deja el ordenador abierto, no puede\nbastar con estar sentado delante para quedarse con la cuenta.\n\nAl cambiarla, TODAS las sesiones abiertas dejan de valer —van firmadas con una huella de\nla contraseña—, así que se emite una nueva para quien la acaba de cambiar. Si no, se\necharía a sí mismo al instante.","operationId":"cambiar_mi_contrasena_api_v1_me_password_post","security":[{"ClaveAPI":[]}],"parameters":[{"name":"koplai_session","in":"cookie","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Koplai Session"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/tenants/{tenant_id}/team/{user_id}/password":{"post":{"tags":["equipo"],"summary":"Reiniciar Contrasena","description":"Poner una contraseña nueva a alguien del equipo, sin conocer la suya.\n\nExiste para el día que alguien deja el negocio o pierde el acceso: la dueña tiene que\npoder resolverlo sola, sin llamar a Koplai. Corta el acceso de inmediato en todos los\ndispositivos donde esa persona tuviera la sesión abierta.\n\nNo sirve contra uno mismo —para eso está /me/password, que sí pide la actual— ni contra\nalguien de rango superior: un ADMIN no puede quedarse con la cuenta del propietario.","operationId":"reiniciar_contrasena_api_v1_tenants__tenant_id__team__user_id__password_post","security":[{"ClaveAPI":[]}],"parameters":[{"name":"tenant_id","in":"path","required":true,"schema":{"type":"string","title":"Tenant Id"}},{"name":"user_id","in":"path","required":true,"schema":{"type":"string","title":"User Id"}},{"name":"koplai_session","in":"cookie","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Koplai Session"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/tenants/{tenant_id}/messages/{message_id}/media":{"get":{"tags":["sesión"],"summary":"Descargar Adjunto","description":"Sirve la foto, el audio o el documento que mandó un cliente.\n\nVa por aquí y no directo a Meta por dos razones: la dirección de Meta exige la credencial\ndel negocio —que no puede pisar el navegador— y porque así el acceso pasa por la misma\nautorización que todo lo demás. Pedir el adjunto de otro negocio da lo mismo que pedir\nuno que no existe.","operationId":"descargar_adjunto_api_v1_tenants__tenant_id__messages__message_id__media_get","security":[{"ClaveAPI":[]}],"parameters":[{"name":"tenant_id","in":"path","required":true,"schema":{"type":"string","title":"Tenant Id"}},{"name":"message_id","in":"path","required":true,"schema":{"type":"string","title":"Message Id"}},{"name":"koplai_session","in":"cookie","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Koplai Session"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"x-faro-scope":"conversations:read"}},"/api/v1/auth/providers":{"get":{"tags":["sesión"],"summary":"Proveedores","description":"Qué formas de entrar están disponibles. La interfaz dibuja solo los botones que sirven:\nenseñar 'Entrar con Google' sin configurarlo es prometer algo que va a fallar.","operationId":"proveedores_api_v1_auth_providers_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/api/v1/auth/social":{"post":{"tags":["sesión"],"summary":"Entrar Con Proveedor","description":"Entrar con Google o con Facebook.\n\nNO crea cuentas: solo deja pasar a quien ya tiene uno de esos proveedores vinculado. Si\ncreara la cuenta al vuelo, cualquiera con una cuenta de Google entraría en la plataforma\n—sin negocio, pero dentro—. Las cuentas nacen por el enlace de alta o porque alguien del\nequipo te añade.","operationId":"entrar_con_proveedor_api_v1_auth_social_post","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/api/v1/auth/claim":{"post":{"tags":["sesión"],"summary":"Reclamar Negocio","description":"Crea la cuenta del propietario de un negocio recién dado de alta.\n\nEl enlace llega al terminar el alta de WhatsApp y vale una sola vez: en cuanto el negocio\ntiene propietario, deja de servir. No hace falta una tabla de enlaces gastados —el propio\nhecho de tener dueño es la marca de usado— y así no hay dos verdades que sincronizar.","operationId":"reclamar_negocio_api_v1_auth_claim_post","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/api/v1/tenants/{tenant_id}/channels/{channel}/connect-url":{"get":{"tags":["canales"],"summary":"Url Para Conectar","description":"A dónde mandar al cliente para que autorice.\n\nEs una redirección normal, no el SDK de JavaScript: así no hay ventana emergente que el\nnavegador pueda bloquear, no hace falta registrar el dominio en un campo aparte de la\nconsola de Meta —cuyo olvido mata el diálogo con \"JSSDK Unknown Host domain\"— y, sobre\ntodo, el token del usuario no pisa el navegador.","operationId":"url_para_conectar_api_v1_tenants__tenant_id__channels__channel__connect_url_get","security":[{"ClaveAPI":[]}],"parameters":[{"name":"tenant_id","in":"path","required":true,"schema":{"type":"string","title":"Tenant Id"}},{"name":"channel","in":"path","required":true,"schema":{"type":"string","title":"Channel"}},{"name":"koplai_session","in":"cookie","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Koplai Session"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/auth/facebook/callback":{"get":{"tags":["sesión"],"summary":"Callback De Facebook","description":"La vuelta de Facebook. Canjea el código y devuelve al cliente a su pantalla de canales.\n\nNO exige sesión de Faro a propósito: el navegador viene de facebook.com y la cookie es\n`SameSite=Lax`, que sí viaja en una navegación de primer nivel como esta —pero apoyarse\nen eso sería frágil. La autorización real la lleva el `state` firmado, que dice qué\nnegocio, qué canal y QUIÉN empezó la conexión.","operationId":"callback_de_facebook_api_v1_auth_facebook_callback_get","parameters":[{"name":"code","in":"query","required":false,"schema":{"type":"string","default":"","title":"Code"}},{"name":"state","in":"query","required":false,"schema":{"type":"string","default":"","title":"State"}},{"name":"error","in":"query","required":false,"schema":{"type":"string","default":"","title":"Error"}},{"name":"error_description","in":"query","required":false,"schema":{"type":"string","default":"","title":"Error Description"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/tenants/{tenant_id}/channels/{channel}/accounts":{"post":{"tags":["canales"],"summary":"Cuentas Del Canal","description":"Las cuentas que esta persona administra y puede conectar.\n\nSe le enseña una lista para ELEGIR: mucha gente administra la página de su negocio y la\nde un familiar, y conectar la equivocada mandaría los mensajes de sus clientes al inbox\nde otro.","operationId":"cuentas_del_canal_api_v1_tenants__tenant_id__channels__channel__accounts_post","security":[{"ClaveAPI":[]}],"parameters":[{"name":"tenant_id","in":"path","required":true,"schema":{"type":"string","title":"Tenant Id"}},{"name":"channel","in":"path","required":true,"schema":{"type":"string","title":"Channel"}},{"name":"koplai_session","in":"cookie","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Koplai Session"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/tenants/{tenant_id}/channels/{channel}/link":{"post":{"tags":["canales"],"summary":"Enlazar Canal","description":"Conecta la cuenta elegida: guarda la credencial y la suscribe a nuestro webhook.","operationId":"enlazar_canal_api_v1_tenants__tenant_id__channels__channel__link_post","security":[{"ClaveAPI":[]}],"parameters":[{"name":"tenant_id","in":"path","required":true,"schema":{"type":"string","title":"Tenant Id"}},{"name":"channel","in":"path","required":true,"schema":{"type":"string","title":"Channel"}},{"name":"koplai_session","in":"cookie","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Koplai Session"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/tenants/{tenant_id}/templates":{"get":{"tags":["plantillas"],"summary":"Listar Plantillas","description":"Lo que el negocio tiene dado de alta, y lo que puede dar de alta.","operationId":"listar_plantillas_api_v1_tenants__tenant_id__templates_get","security":[{"ClaveAPI":[]}],"parameters":[{"name":"tenant_id","in":"path","required":true,"schema":{"type":"string","title":"Tenant Id"}},{"name":"koplai_session","in":"cookie","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Koplai Session"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"x-faro-scope":"templates:send"},"post":{"tags":["plantillas"],"summary":"Crear Plantilla","description":"Da de alta una del catálogo. Queda pendiente hasta que Meta la revisa.","operationId":"crear_plantilla_api_v1_tenants__tenant_id__templates_post","security":[{"ClaveAPI":[]}],"parameters":[{"name":"tenant_id","in":"path","required":true,"schema":{"type":"string","title":"Tenant Id"}},{"name":"koplai_session","in":"cookie","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Koplai Session"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/tenants/{tenant_id}/conversations/{conversation_id}/template":{"post":{"tags":["conversaciones"],"summary":"Enviar Plantilla","description":"Escribe a alguien con una plantilla aprobada.\n\nEs lo que arregla el fallo de fondo: pasadas 24 horas desde el último mensaje del\ncliente, el texto libre lo rechaza Meta y hasta ahora eso era un callejón sin salida.","operationId":"enviar_plantilla_api_v1_tenants__tenant_id__conversations__conversation_id__template_post","security":[{"ClaveAPI":[]}],"parameters":[{"name":"tenant_id","in":"path","required":true,"schema":{"type":"string","title":"Tenant Id"}},{"name":"conversation_id","in":"path","required":true,"schema":{"type":"string","title":"Conversation Id"}},{"name":"koplai_session","in":"cookie","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Koplai Session"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"x-faro-scope":"templates:send"}},"/api/v1/admin/tenants":{"get":{"tags":["admin"],"summary":"Admin Listar","operationId":"admin_listar_api_v1_admin_tenants_get","security":[{"ClaveAPI":[]}],"parameters":[{"name":"koplai_session","in":"cookie","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Koplai Session"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/admin/tenants/{tenant_id}/entitlements":{"patch":{"tags":["admin"],"summary":"Admin Entitlements","description":"Activa o desactiva lo que un cliente ha contratado. Cada cambio se audita con el antes\ny el después: \"quién activó la IA de este negocio y cuándo\" tiene que ser respondible.","operationId":"admin_entitlements_api_v1_admin_tenants__tenant_id__entitlements_patch","security":[{"ClaveAPI":[]}],"parameters":[{"name":"tenant_id","in":"path","required":true,"schema":{"type":"string","title":"Tenant Id"}},{"name":"koplai_session","in":"cookie","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Koplai Session"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/admin/automatizaciones/tick":{"post":{"tags":["admin"],"summary":"Admin Tick","description":"La misma pasada que hace el bucle cada media hora, a demanda. Idempotente.","operationId":"admin_tick_api_v1_admin_automatizaciones_tick_post","security":[{"ClaveAPI":[]}],"parameters":[{"name":"koplai_session","in":"cookie","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Koplai Session"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/admin/tenants/{tenant_id}/correos/{kind}":{"post":{"tags":["admin"],"summary":"Admin Reenviar Correo","description":"Reenvía a mano uno de los correos del alta. Salta la idempotencia con una clave nueva,\nporque quien lo pide ya sabe que se mandó (o que no llegó). Devuelve el estado del envío.","operationId":"admin_reenviar_correo_api_v1_admin_tenants__tenant_id__correos__kind__post","security":[{"ClaveAPI":[]}],"parameters":[{"name":"tenant_id","in":"path","required":true,"schema":{"type":"string","title":"Tenant Id"}},{"name":"kind","in":"path","required":true,"schema":{"type":"string","title":"Kind"}},{"name":"koplai_session","in":"cookie","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Koplai Session"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/admin/tenants/{tenant_id}/disconnect":{"post":{"tags":["admin"],"summary":"Admin Desconectar","description":"Baja de un cliente. Reversible por defecto: desconecta, no destruye.\n\nOrden deliberado: primero se corta la salida (nada más sale hacia sus clientes finales),\nluego la IA, luego el acceso del equipo. Al revés, un agente podría enviar un mensaje\ndurante la baja.\n\nLa conversación NO se borra: es historial del negocio y puede hacer falta para una\nreclamación. `purge=true` lo elimina, y eso sí es irreversible.","operationId":"admin_desconectar_api_v1_admin_tenants__tenant_id__disconnect_post","security":[{"ClaveAPI":[]}],"parameters":[{"name":"tenant_id","in":"path","required":true,"schema":{"type":"string","title":"Tenant Id"}},{"name":"koplai_session","in":"cookie","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Koplai Session"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/tenants/{tenant_id}/channels":{"get":{"tags":["canales"],"summary":"Listar Canales","description":"Los tres canales, estén conectados o no. Un canal que el negocio no ha contratado se\nmuestra igualmente, con su estado: así el cliente sabe que existe y puede pedirlo.","operationId":"listar_canales_api_v1_tenants__tenant_id__channels_get","security":[{"ClaveAPI":[]}],"parameters":[{"name":"tenant_id","in":"path","required":true,"schema":{"type":"string","title":"Tenant Id"}},{"name":"koplai_session","in":"cookie","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Koplai Session"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/tenants/{tenant_id}/channels/{channel}/connect":{"post":{"tags":["canales"],"summary":"Conectar Canal","description":"Registra una conexión de canal ya autorizada en Meta.\n\nRecibe el identificador de la cuenta y el token, y **el token no se guarda en la\naplicación**: se manda al gestor de secretos y en la base de datos queda una referencia.\nSi no se puede guardar de forma segura, la conexión NO se da por buena.","operationId":"conectar_canal_api_v1_tenants__tenant_id__channels__channel__connect_post","security":[{"ClaveAPI":[]}],"parameters":[{"name":"tenant_id","in":"path","required":true,"schema":{"type":"string","title":"Tenant Id"}},{"name":"channel","in":"path","required":true,"schema":{"type":"string","title":"Channel"}},{"name":"koplai_session","in":"cookie","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Koplai Session"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/tenants/{tenant_id}/channels/{channel}/disconnect":{"post":{"tags":["canales"],"summary":"Desconectar Canal","operationId":"desconectar_canal_api_v1_tenants__tenant_id__channels__channel__disconnect_post","security":[{"ClaveAPI":[]}],"parameters":[{"name":"tenant_id","in":"path","required":true,"schema":{"type":"string","title":"Tenant Id"}},{"name":"channel","in":"path","required":true,"schema":{"type":"string","title":"Channel"}},{"name":"koplai_session","in":"cookie","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Koplai Session"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/tenants/{tenant_id}/booking/services":{"get":{"tags":["reservas"],"summary":"Listar Servicios","operationId":"listar_servicios_api_v1_tenants__tenant_id__booking_services_get","security":[{"ClaveAPI":[]}],"parameters":[{"name":"tenant_id","in":"path","required":true,"schema":{"type":"string","title":"Tenant Id"}},{"name":"koplai_session","in":"cookie","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Koplai Session"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"x-faro-scope":"agent:tools"},"post":{"tags":["reservas"],"summary":"Crear Servicio","operationId":"crear_servicio_api_v1_tenants__tenant_id__booking_services_post","security":[{"ClaveAPI":[]}],"parameters":[{"name":"tenant_id","in":"path","required":true,"schema":{"type":"string","title":"Tenant Id"}},{"name":"koplai_session","in":"cookie","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Koplai Session"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/tenants/{tenant_id}/booking/services/{service_id}":{"patch":{"tags":["reservas"],"summary":"Editar Servicio","operationId":"editar_servicio_api_v1_tenants__tenant_id__booking_services__service_id__patch","security":[{"ClaveAPI":[]}],"parameters":[{"name":"tenant_id","in":"path","required":true,"schema":{"type":"string","title":"Tenant Id"}},{"name":"service_id","in":"path","required":true,"schema":{"type":"string","title":"Service Id"}},{"name":"koplai_session","in":"cookie","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Koplai Session"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/tenants/{tenant_id}/booking/staff":{"get":{"tags":["reservas"],"summary":"Listar Personal","operationId":"listar_personal_api_v1_tenants__tenant_id__booking_staff_get","security":[{"ClaveAPI":[]}],"parameters":[{"name":"tenant_id","in":"path","required":true,"schema":{"type":"string","title":"Tenant Id"}},{"name":"koplai_session","in":"cookie","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Koplai Session"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"x-faro-scope":"agent:tools"},"post":{"tags":["reservas"],"summary":"Crear Personal","operationId":"crear_personal_api_v1_tenants__tenant_id__booking_staff_post","security":[{"ClaveAPI":[]}],"parameters":[{"name":"tenant_id","in":"path","required":true,"schema":{"type":"string","title":"Tenant Id"}},{"name":"koplai_session","in":"cookie","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Koplai Session"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/tenants/{tenant_id}/booking/staff/{staff_id}":{"patch":{"tags":["reservas"],"summary":"Editar Personal","operationId":"editar_personal_api_v1_tenants__tenant_id__booking_staff__staff_id__patch","security":[{"ClaveAPI":[]}],"parameters":[{"name":"tenant_id","in":"path","required":true,"schema":{"type":"string","title":"Tenant Id"}},{"name":"staff_id","in":"path","required":true,"schema":{"type":"string","title":"Staff Id"}},{"name":"koplai_session","in":"cookie","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Koplai Session"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/tenants/{tenant_id}/booking/hours":{"get":{"tags":["reservas"],"summary":"Ver Horario","operationId":"ver_horario_api_v1_tenants__tenant_id__booking_hours_get","security":[{"ClaveAPI":[]}],"parameters":[{"name":"tenant_id","in":"path","required":true,"schema":{"type":"string","title":"Tenant Id"}},{"name":"koplai_session","in":"cookie","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Koplai Session"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"x-faro-scope":"agent:tools"},"put":{"tags":["reservas"],"summary":"Fijar Horario","operationId":"fijar_horario_api_v1_tenants__tenant_id__booking_hours_put","security":[{"ClaveAPI":[]}],"parameters":[{"name":"tenant_id","in":"path","required":true,"schema":{"type":"string","title":"Tenant Id"}},{"name":"koplai_session","in":"cookie","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Koplai Session"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/tenants/{tenant_id}/booking/availability":{"get":{"tags":["reservas"],"summary":"Ver Disponibilidad","operationId":"ver_disponibilidad_api_v1_tenants__tenant_id__booking_availability_get","security":[{"ClaveAPI":[]}],"parameters":[{"name":"tenant_id","in":"path","required":true,"schema":{"type":"string","title":"Tenant Id"}},{"name":"service_id","in":"query","required":true,"schema":{"type":"string","title":"Service Id"}},{"name":"date","in":"query","required":true,"schema":{"type":"string","title":"Date"}},{"name":"koplai_session","in":"cookie","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Koplai Session"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"x-faro-scope":"agent:tools"}},"/api/v1/tenants/{tenant_id}/booking/appointments":{"get":{"tags":["reservas"],"summary":"Listar Citas","operationId":"listar_citas_api_v1_tenants__tenant_id__booking_appointments_get","security":[{"ClaveAPI":[]}],"parameters":[{"name":"tenant_id","in":"path","required":true,"schema":{"type":"string","title":"Tenant Id"}},{"name":"desde","in":"query","required":false,"schema":{"type":"string","default":"","title":"Desde"}},{"name":"hasta","in":"query","required":false,"schema":{"type":"string","default":"","title":"Hasta"}},{"name":"koplai_session","in":"cookie","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Koplai Session"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"x-faro-scope":"agent:tools"},"post":{"tags":["reservas"],"summary":"Crear Cita","operationId":"crear_cita_api_v1_tenants__tenant_id__booking_appointments_post","security":[{"ClaveAPI":[]}],"parameters":[{"name":"tenant_id","in":"path","required":true,"schema":{"type":"string","title":"Tenant Id"}},{"name":"koplai_session","in":"cookie","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Koplai Session"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"x-faro-scope":"agent:tools"}},"/api/v1/tenants/{tenant_id}/booking/appointments/{appointment_id}":{"patch":{"tags":["reservas"],"summary":"Cambiar Cita","operationId":"cambiar_cita_api_v1_tenants__tenant_id__booking_appointments__appointment_id__patch","security":[{"ClaveAPI":[]}],"parameters":[{"name":"tenant_id","in":"path","required":true,"schema":{"type":"string","title":"Tenant Id"}},{"name":"appointment_id","in":"path","required":true,"schema":{"type":"string","title":"Appointment Id"}},{"name":"koplai_session","in":"cookie","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Koplai Session"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"x-faro-scope":"agent:tools"}},"/api/v1/tenants/{tenant_id}/agent":{"get":{"tags":["agente"],"summary":"Ver Agente","operationId":"ver_agente_api_v1_tenants__tenant_id__agent_get","security":[{"ClaveAPI":[]}],"parameters":[{"name":"tenant_id","in":"path","required":true,"schema":{"type":"string","title":"Tenant Id"}},{"name":"koplai_session","in":"cookie","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Koplai Session"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"patch":{"tags":["agente"],"summary":"Configurar Agente","operationId":"configurar_agente_api_v1_tenants__tenant_id__agent_patch","security":[{"ClaveAPI":[]}],"parameters":[{"name":"tenant_id","in":"path","required":true,"schema":{"type":"string","title":"Tenant Id"}},{"name":"koplai_session","in":"cookie","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Koplai Session"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/tenants/{tenant_id}/capabilities":{"get":{"tags":["tenants"],"summary":"Ver Capacidades","operationId":"ver_capacidades_api_v1_tenants__tenant_id__capabilities_get","security":[{"ClaveAPI":[]}],"parameters":[{"name":"tenant_id","in":"path","required":true,"schema":{"type":"string","title":"Tenant Id"}},{"name":"koplai_session","in":"cookie","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Koplai Session"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/admin/tenants/{tenant_id}/capabilities":{"patch":{"tags":["admin"],"summary":"Admin Capacidades","description":"Concesión manual por Koplai. Es el punto donde encajará Stripe sin tocar nada más.","operationId":"admin_capacidades_api_v1_admin_tenants__tenant_id__capabilities_patch","security":[{"ClaveAPI":[]}],"parameters":[{"name":"tenant_id","in":"path","required":true,"schema":{"type":"string","title":"Tenant Id"}},{"name":"koplai_session","in":"cookie","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Koplai Session"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/tenants/{tenant_id}/api-keys":{"post":{"tags":["claves-api"],"summary":"Crear Clave Api","description":"La clave se devuelve UNA vez, aquí. No se guarda: solo su hash.","operationId":"crear_clave_api_api_v1_tenants__tenant_id__api_keys_post","security":[{"FaroSession":[]}],"parameters":[{"name":"tenant_id","in":"path","required":true,"schema":{"type":"string","title":"Tenant Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiKeyCreate"}}}},"responses":{"201":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"get":{"tags":["claves-api"],"summary":"Listar Claves Api","operationId":"listar_claves_api_api_v1_tenants__tenant_id__api_keys_get","security":[{"FaroSession":[]}],"parameters":[{"name":"tenant_id","in":"path","required":true,"schema":{"type":"string","title":"Tenant Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/tenants/{tenant_id}/api-keys/{key_id}":{"delete":{"tags":["claves-api"],"summary":"Revocar Clave Api","operationId":"revocar_clave_api_api_v1_tenants__tenant_id__api_keys__key_id__delete","security":[{"FaroSession":[]}],"parameters":[{"name":"tenant_id","in":"path","required":true,"schema":{"type":"string","title":"Tenant Id"}},{"name":"key_id","in":"path","required":true,"schema":{"type":"string","title":"Key Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/tenants/{tenant_id}/contacts":{"get":{"tags":["contactos"],"summary":"Listar Contactos","operationId":"listar_contactos_api_v1_tenants__tenant_id__contacts_get","security":[{"ClaveAPI":[]}],"parameters":[{"name":"tenant_id","in":"path","required":true,"schema":{"type":"string","title":"Tenant Id"}},{"name":"q","in":"query","required":false,"schema":{"type":"string","default":"","title":"Q"}},{"name":"koplai_session","in":"cookie","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Koplai Session"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"x-faro-scope":"contacts:read"}},"/webhook":{"get":{"summary":"Verify","operationId":"verify_webhook_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}},"post":{"summary":"Inbound","description":"Ingesta única para los tres productos de Meta.\n\nSemántica HTTP — deliberada, porque Meta reintenta durante 7 días ante cualquier no-2xx:\n  403  la firma no es válida. SOLO eso.\n  400  viene firmado pero el cuerpo no es JSON.\n  200  aceptado: procesado, duplicado, ignorado, o rechazado en firme.\n  500  fallo transitorio NUESTRO. Meta reintenta y el evento queda reejecutable.","operationId":"inbound_webhook_post","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/telegram-callback":{"post":{"summary":"Telegram Callback","description":"Maneja los botones del aviso al dueño (Enviar / Editar / Ignorar) y aprobación de artículos del blog.","operationId":"telegram_callback_telegram_callback_post","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/api/whatsapp-onboard":{"post":{"summary":"Onboard","description":"Alta de cliente desde el Embedded Signup.\nBody: {code, waba_id, phone_number_id?, mode?: \"dedicated\"|\"coexistence\",\n       business_name?, services?, hours?}\n\n`mode` por defecto es `dedicated` (número nuevo: se registra con PIN). En `coexistence`\nel FINISH de Meta (`FINISH_WHATSAPP_BUSINESS_APP_ONBOARDING`) solo trae `waba_id`, así\nque `phone_number_id` puede faltar y se resuelve en el servidor. En `dedicated` sigue\nsiendo obligatorio: una WABA puede tener varios números y hay que saber cuál.","operationId":"onboard_api_whatsapp_onboard_post","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/tenants":{"get":{"summary":"List Tenants","operationId":"list_tenants_tenants_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/api/tenant-business":{"post":{"summary":"Set Business","description":"Cargar/actualizar servicios, precios y horario de un cliente (lo que usa el agente).\nBody: {phone_number_id, business_name?, services?, hours?, timezone?}","operationId":"set_business_api_tenant_business_post","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/api/web-chat":{"post":{"summary":"Web Chat","description":"Chat embebido en koplai.com — el agente de Koplai responde en la propia web.","operationId":"web_chat_api_web_chat_post","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/api/web-chat/poll":{"post":{"summary":"Web Chat Poll","description":"Lo que una PERSONA ha escrito desde el inbox a esta sesión del navegador.\n\nSolo mensajes con provenance='human': lo que responde el agente ya viaja en la\nrespuesta de /api/web-chat, y devolverlo otra vez lo duplicaría en pantalla.\nEl id de sesión lo genera el navegador (crypto.randomUUID) y acota la búsqueda a\ncontactos 'web:', así que esto no puede usarse para leer conversaciones de WhatsApp.","operationId":"web_chat_poll_api_web_chat_poll_post","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/api/templates":{"get":{"summary":"Templates List","description":"Plantillas del cliente y su estado (APPROVED / PENDING / REJECTED).","operationId":"templates_list_api_templates_get","parameters":[{"name":"phone_number_id","in":"query","required":false,"schema":{"type":"string","default":"","title":"Phone Number Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"post":{"summary":"Templates Create","description":"Crea una plantilla y la manda a aprobación.\nBody: {phone_number_id, name, category, body, language?, header?, footer?, example_body?}","operationId":"templates_create_api_templates_post","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}},"delete":{"summary":"Templates Delete","operationId":"templates_delete_api_templates_delete","parameters":[{"name":"phone_number_id","in":"query","required":false,"schema":{"type":"string","default":"","title":"Phone Number Id"}},{"name":"name","in":"query","required":false,"schema":{"type":"string","default":"","title":"Name"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/send-template":{"post":{"summary":"Send Template Route","description":"Envía una plantilla ya aprobada — esto SÍ funciona fuera de la ventana de 24 h.\nBody: {phone_number_id, to, name, language?, components?}\n`to` acepta teléfono o BSUID; whatsapp.py elige el campo correcto de la API.","operationId":"send_template_route_api_send_template_post","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/admin":{"get":{"summary":"Admin Page","operationId":"admin_page_admin_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/api/tenants":{"get":{"summary":"Api Tenants","operationId":"api_tenants_api_tenants_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/api/tenant":{"get":{"summary":"Api Tenant","description":"Ficha completa de un cliente para el panel. Nunca devuelve el access_token.","operationId":"api_tenant_api_tenant_get","parameters":[{"name":"phone_number_id","in":"query","required":false,"schema":{"type":"string","default":"","title":"Phone Number Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/health":{"get":{"summary":"Health","operationId":"health_health_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/blog-preview/{article_id}":{"get":{"summary":"Blog Preview","description":"Previsualización de draft de blog — solo para JP (no indexado).","operationId":"blog_preview_blog_preview__article_id__get","parameters":[{"name":"article_id","in":"path","required":true,"schema":{"type":"string","title":"Article Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/assets/{resto}":{"get":{"summary":"Ui Assets","operationId":"ui_assets_assets__resto__get","parameters":[{"name":"resto","in":"path","required":true,"schema":{"type":"string","title":"Resto"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/favicon-32.png":{"get":{"summary":"Ui Raiz","operationId":"ui_raiz_favicon_32_png_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/apple-touch-icon.png":{"get":{"summary":"Ui Raiz","operationId":"ui_raiz_apple_touch_icon_png_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/icon-maskable.svg":{"get":{"summary":"Ui Raiz","operationId":"ui_raiz_icon_maskable_svg_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/icon-maskable-512.png":{"get":{"summary":"Ui Raiz","operationId":"ui_raiz_icon_maskable_512_png_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/icon-512.png":{"get":{"summary":"Ui Raiz","operationId":"ui_raiz_icon_512_png_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/icon-192.png":{"get":{"summary":"Ui Raiz","operationId":"ui_raiz_icon_192_png_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/logo-mark.svg":{"get":{"summary":"Ui Raiz","operationId":"ui_raiz_logo_mark_svg_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/manifest.webmanifest":{"get":{"summary":"Ui Raiz","operationId":"ui_raiz_manifest_webmanifest_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/sw.js":{"get":{"summary":"Ui Raiz","operationId":"ui_raiz_sw_js_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/":{"get":{"summary":"Raiz","description":"La raíz entra en el inbox. Si no hay sesión, la propia aplicación pide credenciales.","operationId":"raiz__get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/app/{resto}":{"get":{"summary":"Interfaz Heredada","description":"Ruta histórica. Redirige a la canónica en vez de servir una segunda copia: dos URLs para\nla misma pantalla acaban en enlaces compartidos que apuntan al sitio equivocado.\n\nSolo afecta a /app. `/webhook`, `/api` y el resto de endpoints de infraestructura no se\ntocan: son los que Meta y los scripts ya usan en producción.","operationId":"interfaz_heredada_app__resto__get","parameters":[{"name":"resto","in":"path","required":true,"schema":{"type":"string","title":"Resto"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/app":{"get":{"summary":"Interfaz Heredada","description":"Ruta histórica. Redirige a la canónica en vez de servir una segunda copia: dos URLs para\nla misma pantalla acaban en enlaces compartidos que apuntan al sitio equivocado.\n\nSolo afecta a /app. `/webhook`, `/api` y el resto de endpoints de infraestructura no se\ntocan: son los que Meta y los scripts ya usan en producción.","operationId":"interfaz_heredada_app_get","parameters":[{"name":"resto","in":"query","required":false,"schema":{"type":"string","default":"","title":"Resto"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}}},"components":{"schemas":{"ApiKeyCreate":{"properties":{"name":{"type":"string","maxLength":80,"title":"Name","description":"Integration name, never a secret"},"scopes":{"items":{"type":"string"},"type":"array","title":"Scopes","description":"conversations:read, conversations:write, contacts:read, templates:send, agent:tools. webhooks:manage is reserved and has no usable routes. Start read-only."}},"additionalProperties":false,"type":"object","required":["name"],"title":"ApiKeyCreate"},"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":{"ClaveAPI":{"type":"http","description":"Clave de API por tenant: `Authorization: Bearer fk_live_…`","scheme":"bearer"},"FaroSession":{"type":"apiKey","in":"cookie","name":"koplai_session"}}}}