{"openapi":"3.0.3","info":{"title":"API de Clonify","version":"1.0.0","description":"Conecta tu sistema (tu CRM, tu app, Make…) con tu cuenta de Clonify: lee tus conversaciones, contactos y transcripciones, y construye desde fuera. Sube contenido a tu Conocimiento y crea productos, preguntas frecuentes y clones. La API habla JSON sobre HTTPS y usa las mismas reglas que el panel: tus cuotas y candados de plan se respetan solos.\n\nCada cuenta tiene un cupo de peticiones por minuto según su plan: 60 (Starter), 120 (Pro) o 300 (Scale), compartido entre todas sus llaves. Cada respuesta incluye las cabeceras X-RateLimit-Limit, X-RateLimit-Remaining y X-RateLimit-Reset. Al superarlo recibes 429 con Retry-After (espera y reintenta con backoff). El límite corta scripts descontrolados, no uso real: a 120/min se exportan 12.000 conversaciones por minuto."},"servers":[{"url":"https://www.clonify.com/api/v1"}],"tags":[{"name":"Seguimiento de alumnos"},{"name":"Entregas externas por API"},{"name":"Chat con el clon"},{"name":"Contactos y etiquetas"},{"name":"Accesos de academias"},{"name":"Conversaciones"},{"name":"Catálogo de productos"},{"name":"Ventas externas"},{"name":"Conocimiento"},{"name":"Multimedia"},{"name":"Clones (versions en la API)"},{"name":"Reuniones y reservas"},{"name":"Webinars"},{"name":"Cuenta y consumo"}],"paths":{"/products/{id}/students":{"get":{"operationId":"product_students_list","tags":["Seguimiento de alumnos"],"summary":"Listar los alumnos de un curso y cómo van","description":"Quién tiene acceso al curso y en qué estado está cada uno: activo (alguna señal de estudio en los últimos 7 días), en riesgo (7 a 13 días sin actividad), parado (14 o más), sin empezar (sin ninguna actividad) o terminado (todas las clases publicadas marcadas). Cuenta como actividad marcar una clase, abrirla, ver su vídeo, asistir a un directo del curso o ver su grabación. Incluye la última actividad con su clase o directo, las clases marcadas y las empezadas sin marcar, el acceso y un resumen del curso entero. Son los mismos datos que la pestaña Alumnos del panel.","security":[{"apiKey":[]}],"parameters":[{"name":"status","in":"query","required":false,"description":"Solo los de un estado; attention = los que necesitan un empujón (en riesgo, parados de menos de 90 días y sin empezar con acceso desde hace 7 a 29 días).","schema":{"type":"string"}},{"name":"sort","in":"query","required":false,"description":"Orden. attention (por defecto) pone primero a quien necesita un empujón; recent/oldest, por fecha de alta; progress, por clases marcadas; activity, por última actividad.","schema":{"type":"string"}},{"name":"email","in":"query","required":false,"description":"Busca por correo o nombre dentro del curso.","schema":{"type":"string"}},{"name":"starting_after","in":"query","required":false,"description":"Cursor recibido en next_starting_after. Omitir en la primera página.","schema":{"type":"string"}},{"name":"limit","in":"query","required":false,"description":"Máximo de alumnos por página: 1–200; predeterminado 50.","schema":{"type":"integer"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"examples":{"example":{"value":"{\n  \"object\": \"list\",\n  \"data\": [\n    {\n      \"object\": \"course_student\",\n      \"visitor_id\": 88,\n      \"contact_id\": 135,\n      \"email\": \"student@example.test\",\n      \"name\": \"Student\",\n      \"status\": \"active\",\n      \"status_days\": 1,\n      \"needs_attention\": false,\n      \"last_activity_at\": \"2026-10-13T21:36:00Z\",\n      \"last_activity\": {\n        \"kind\": \"video\",\n        \"at\": \"2026-10-13T21:36:00Z\",\n        \"lesson_id\": 1004,\n        \"lesson_title\": \"Lesson 4\",\n        \"live_id\": null,\n        \"live_title\": null\n      },\n      \"last_marked_at\": \"2026-09-22T10:00:00Z\",\n      \"completed_lessons\": 3,\n      \"total_lessons\": 12,\n      \"completed_lesson_ids\": [\n        1001,\n        1002,\n        1003\n      ],\n      \"started_lesson_ids\": [\n        1004\n      ],\n      \"access\": {\n        \"source\": \"purchase\",\n        \"by_tag\": true,\n        \"included_from_product_ids\": [],\n        \"granted_at\": \"2026-08-30T10:00:00Z\",\n        \"expires_at\": null,\n        \"paused\": false\n      }\n    }\n  ],\n  \"has_more\": true,\n  \"next_starting_after\": \"bzo1MA\",\n  \"summary\": {\n    \"total\": 14,\n    \"matching\": 14,\n    \"by_status\": {\n      \"active\": 5,\n      \"at_risk\": 3,\n      \"stalled\": 3,\n      \"not_started\": 2,\n      \"completed\": 1\n    },\n    \"needs_attention\": 7,\n    \"at_risk_after_days\": 7,\n    \"stalled_after_days\": 14\n  },\n  \"measurement\": {\n    \"activity\": \"Any study signal in this course: completing a lesson, opening it, watching its video, attending one of the course's live sessions or watching its recording.\",\n    \"coverage\": \"Lesson opens and video watching are recorded since 2026-09-24 and only for campus and Bunny players (not YouTube, Vimeo or other sites); earlier, only completed lessons exist.\",\n    \"paging\": \"Pages follow the requested order at request time; if students change between calls a page may repeat or skip someone. Re-read all pages to refresh.\"\n  }\n}"}}}}}}}},"/learning/playback":{"get":{"operationId":"learning_playback","tags":["Seguimiento de alumnos"],"summary":"Consultar reproducción de vídeos","description":"Muestras nuevas de los reproductores observables de clases, emisiones y repeticiones (Bunny y Mux). Requiere exactamente product_id para clases o webinar_id para directos/repeticiones. Cada fila contiene incrementos, no totales: visible_playing_seconds estima reproducción con pestaña visible y media_seconds avance reproducido, ajustado por velocidad cuando está disponible. Las repeticiones vuelven a sumar; no es porcentaje único visto. Saltar de posición no acredita el tramo saltado. pause_count cuenta pausas y seek_count saltos/discontinuidades observadas; position_seconds es una posición, no tiempo visto. Campos enviados por el navegador y limitados con reloj del servidor; no prueban atención ni impiden toda falsificación. visitor_id identifica sesión verificada; registrant_id sin visitante corresponde a enlace de inscripción, que puede reenviarse. No se unifican automáticamente ambos tipos de identidad. Se excluyen pausas, pestañas ocultas y huecos largos; puede faltar el final al cerrar o perder conexión. Los iframes sin velocidad verificable se miden conservadoramente a 1x y pueden infracontar o marcar discontinuidades a otras velocidades. No incluye audio ni otros proveedores externos sin adaptador. Los permisos de envío duran hasta 12 horas desde el acceso autorizado y no conceden acceso al vídeo. resource identifica el contenido observado. No reconstruye históricos, no cambia completados y no certifica FUNDAE. El cursor sirve para nuevas muestras; sin filtros de fecha o alumno, filtra esos campos en el cliente.","security":[{"apiKey":[]}],"parameters":[{"name":"product_id","in":"query","required":false,"description":"Curso. Indicar este parámetro o webinar_id, nunca ambos.","schema":{"type":"integer"}},{"name":"webinar_id","in":"query","required":false,"description":"Directo cuya emisión o repetición se consulta. Nunca se atribuye automáticamente a un curso.","schema":{"type":"integer"}},{"name":"starting_after","in":"query","required":false,"description":"ID de la última muestra recibida para continuar la exportación.","schema":{"type":"integer"}},{"name":"limit","in":"query","required":false,"description":"Máximo de muestras: 1–200; predeterminado 100.","schema":{"type":"integer"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"examples":{"example":{"value":"{\n  \"object\": \"list\",\n  \"data\": [\n    {\n      \"id\": \"1\",\n      \"session_id\": \"6e2c91cc-15a5-4c46-a5cd-1db7703c1d20\",\n      \"visitor_id\": 88,\n      \"registrant_id\": null,\n      \"kind\": \"course\",\n      \"product_id\": 100,\n      \"unit_id\": 1000,\n      \"block_id\": 1001,\n      \"webinar_id\": null,\n      \"resource\": \"content-fingerprint\",\n      \"occurred_at\": \"2026-09-24T12:00:30Z\",\n      \"visible_playing_seconds\": 30,\n      \"media_seconds\": 60,\n      \"position_seconds\": 90,\n      \"pause_count\": 0,\n      \"seek_count\": 1,\n      \"closed\": false\n    }\n  ],\n  \"has_more\": false,\n  \"next_starting_after\": null,\n  \"measurement\": {\n    \"source\": \"visible_player_client_reported\",\n    \"limits\": \"Not proof of attention. Media seconds include repeats, not unique coverage. Missing telemetry is not reconstructed. Server caps concurrent time per known visitor or registration.\"\n  }\n}"}}}}}}}},"/learning/attendance":{"get":{"operationId":"learning_attendance","tags":["Seguimiento de alumnos"],"summary":"Consultar conexiones a mesas de grupo","description":"Evidencias nuevas desde la activación, confirmadas por avisos firmados de LiveKit. Una fila por inscripción con las conexiones a la sala principal y sus salas de grupo. confirmed_connected_seconds suma intervalos completos sin duplicar solapamientos entre dispositivos o salas de una misma inscripción. entry_only y exit_only indican límites desconocidos y no suman duración; closed tiene entrada y salida observadas. No incluye emisiones, visitas a la página ni espera en la puerta. identity_source es registration_link o verified_session; visitor_id solo se vincula con sesión verificada del mismo correo y academia. Un enlace reenviado no demuestra quién está detrás. audience_product_ids conserva el público configurado, no acredita matrícula ni asigna minutos a cursos. No demuestra atención ni cumplimiento FUNDAE. No reconstruye históricos ni garantiza conservación documental. No admite filtros de fecha o product_id: para la asistencia de todas las sesiones de un curso entre dos fechas, con estos segundos ya sumados, usa GET /api/v1/webinars/attendance. Vuelve a consultar todas las páginas para actualizar conexiones, el cursor no ordena cambios.","security":[{"apiKey":[]}],"parameters":[{"name":"webinar_id","in":"query","required":true,"description":"Identificador de la mesa de grupo. Obligatorio.","schema":{"type":"integer"}},{"name":"visitor_id","in":"query","required":false,"description":"Filtra inscripciones vinculadas a este alumno verificado o sus identidades del mismo correo en la academia. Cada conexión conserva su propia fuente de identidad.","schema":{"type":"integer"}},{"name":"starting_after","in":"query","required":false,"description":"ID de inscripción recibido en next_starting_after. Omitir en la primera página.","schema":{"type":"integer"}},{"name":"limit","in":"query","required":false,"description":"Máximo de inscripciones: 1–100; predeterminado 50.","schema":{"type":"integer"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"examples":{"example":{"value":"{\n  \"object\": \"list\",\n  \"data\": [\n    {\n      \"id\": \"10\",\n      \"registrant_id\": 10,\n      \"registrant_email\": \"student@example.test\",\n      \"registrant_name\": \"Student\",\n      \"webinar_id\": 50,\n      \"confirmed_connected_seconds\": 120,\n      \"has_incomplete_connections\": false,\n      \"connections\": [\n        {\n          \"room_name\": \"webinar-example1\",\n          \"room_sid\": \"RM_example\",\n          \"participant_sid\": \"PA_example\",\n          \"visitor_id\": 88,\n          \"identity_source\": \"verified_session\",\n          \"audience_product_ids\": [\n            100\n          ],\n          \"joined_at\": \"2026-09-24T12:00:00Z\",\n          \"left_at\": \"2026-09-24T12:02:00Z\",\n          \"connected_seconds\": 120,\n          \"join_event_id\": \"event-join\",\n          \"leave_event_id\": \"event-leave\",\n          \"status\": \"closed\"\n        }\n      ]\n    }\n  ],\n  \"has_more\": false,\n  \"next_starting_after\": null,\n  \"measurement\": {\n    \"source\": \"signed_livekit_webhooks\",\n    \"scope\": \"group_sessions_only\",\n    \"duration\": \"union of observed closed intervals per registration; incomplete intervals excluded\",\n    \"limitations\": \"connection evidence, not attention or learning; forwarded links cannot prove the human identity\",\n    \"refresh\": \"re-read all pages for the webinar; registrant IDs are not a changes cursor\"\n  }\n}"}}}}}}}},"/learning/events":{"get":{"operationId":"learning_events","tags":["Seguimiento de alumnos"],"summary":"Consultar evidencias de aprendizaje","description":"Historial nuevo desde la activación: lesson_opened, lesson_completed, lesson_uncompleted, quiz_submitted y assignment_submitted. También exam_started, exam_submitted y exam_reviewed para exámenes independientes: entrega y revisiones conservan preguntas, respuestas y notas; review_status distingue notas pendientes de revisión. certificate_issued conserva cada emisión y sus requisitos comprobados; certificate_pdf_generated identifica el PDF generado con SHA-256, sin confirmar recepción. Los exámenes se consultan por su propio product_id, no se asignan automáticamente a un curso. No reconstruye actividad anterior, no conserva los archivos por sí mismo ni certifica cumplimiento FUNDAE. En cuestionarios, source distingue server_graded de legacy_client_reported.","security":[{"apiKey":[]}],"parameters":[{"name":"product_id","in":"query","required":true,"description":"Curso o ficha interna del examen que se consulta. Obligatorio; los exámenes independientes usan su propio identificador.","schema":{"type":"integer"}},{"name":"visitor_id","in":"query","required":false,"description":"Alumno; incluye identidades con el mismo correo dentro de la academia.","schema":{"type":"integer"}},{"name":"starting_after","in":"query","required":false,"description":"ID de paginación recibido en next_starting_after. Omitir en la primera página.","schema":{"type":"integer"}},{"name":"limit","in":"query","required":false,"description":"Máximo de registros: 1–200; predeterminado 100.","schema":{"type":"integer"}},{"name":"from","in":"query","required":false,"description":"Fecha inclusiva con zona horaria. En sesiones filtra la fecha de inicio; no recorta sus segundos.","schema":{"type":"string"}},{"name":"until","in":"query","required":false,"description":"Fecha exclusiva con zona horaria. En sesiones filtra la fecha de inicio; no recorta sus segundos.","schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"examples":{"example":{"value":"{\n  \"object\": \"list\",\n  \"data\": [\n    {\n      \"id\": \"1\",\n      \"visitor_id\": 88,\n      \"visitor_email\": \"student@example.test\",\n      \"product_id\": 100,\n      \"unit_id\": null,\n      \"block_id\": null,\n      \"kind\": \"certificate_pdf_generated\",\n      \"occurred_at\": \"2026-09-24T12:00:00Z\",\n      \"evidence\": {\n        \"source\": \"pdf_server\",\n        \"verification_code\": \"example-code\",\n        \"issued_at\": \"2026-09-24T11:00:00Z\",\n        \"sha256\": \"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\",\n        \"bytes\": 24000,\n        \"receipt_confirmed\": false\n      }\n    }\n  ],\n  \"has_more\": false,\n  \"next_starting_after\": null\n}"}}}}}}}},"/learning/sessions":{"get":{"operationId":"learning_sessions","tags":["Seguimiento de alumnos"],"summary":"Consultar sesiones de clase","description":"Tiempo estimado con la clase visible y tiempo activo con foco e interacción reciente. No equivale a vídeo reproducido ni acredita aprendizaje. No acumula pestañas ocultas y limita la suma entre dispositivos. Estados open, closed y timed_out. Las filas pueden actualizarse: vuelve a consultar el periodo completo para refrescar sesiones; el cursor ordena creaciones, no cambios.","security":[{"apiKey":[]}],"parameters":[{"name":"product_id","in":"query","required":true,"description":"Curso que se consulta. Obligatorio.","schema":{"type":"integer"}},{"name":"visitor_id","in":"query","required":false,"description":"Alumno; incluye identidades con el mismo correo dentro de la academia.","schema":{"type":"integer"}},{"name":"starting_after","in":"query","required":false,"description":"ID de paginación recibido en next_starting_after. Omitir en la primera página.","schema":{"type":"integer"}},{"name":"limit","in":"query","required":false,"description":"Máximo de registros: 1–200; predeterminado 100.","schema":{"type":"integer"}},{"name":"from","in":"query","required":false,"description":"Fecha inclusiva con zona horaria. En sesiones filtra la fecha de inicio; no recorta sus segundos.","schema":{"type":"string"}},{"name":"until","in":"query","required":false,"description":"Fecha exclusiva con zona horaria. En sesiones filtra la fecha de inicio; no recorta sus segundos.","schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"examples":{"example":{"value":"{\n  \"object\": \"list\",\n  \"data\": [\n    {\n      \"id\": \"1\",\n      \"session_id\": \"6e2c91cc-15a5-4c46-a5cd-1db7703c1d20\",\n      \"visitor_id\": 88,\n      \"visitor_email\": \"student@example.test\",\n      \"product_id\": 100,\n      \"unit_id\": 1000,\n      \"started_at\": \"2026-09-23T12:00:00Z\",\n      \"last_seen_at\": \"2026-09-23T12:01:00Z\",\n      \"ended_at\": null,\n      \"connected_seconds\": 60,\n      \"active_seconds\": 30,\n      \"status\": \"open\"\n    }\n  ],\n  \"has_more\": false,\n  \"next_starting_after\": null,\n  \"measurement\": {\n    \"active_seconds\": \"visible activity estimate; not video watch time or proof of learning\",\n    \"collection_started\": \"deployment; no historical reconstruction\"\n  }\n}"}}}}}}}},"/products/{id}/feedback/responses":{"get":{"operationId":"product_feedback_responses_list","tags":["Seguimiento de alumnos"],"summary":"Listar respuestas de feedback de un curso","description":"Las respuestas de los alumnos a las encuestas de feedback del curso: la de «En todas las clases» (survey course) y las propias de una clase (survey lesson). Son las mismas filas que el CSV de la pestaña Feedback. Van de la última respuesta a la más antigua. Cada una trae el id del alumno (visitor_id, y contact_id si tiene ficha en Contactos), de la clase, del módulo, de la encuesta y de cada pregunta. Si el alumno cambia su respuesta, cambia esa misma fila (mismo id, nuevo updated_at). No se devuelven respuestas a preguntas que ya no están en la encuesta. Una respuesta que cambie mientras recorres las páginas sube arriba: si aún no te había llegado, esa pasada no la trae, y si era la del cursor, se repiten respuestas. Al terminar, vuelve a pedir con since igual al updated_at más reciente que recibiste para recoger esos cambios, y deduplica por id. 404 si el producto no es un curso de tu cuenta.","security":[{"apiKey":[]}],"parameters":[{"name":"id","in":"path","required":true,"description":"Id del curso.","schema":{"type":"integer"}},{"name":"since","in":"query","required":false,"description":"Solo respuestas cambiadas después de esta fecha (excluida), con zona horaria. Para traer solo lo nuevo, usa la fecha de la sincronización anterior.","schema":{"type":"string"}},{"name":"until","in":"query","required":false,"description":"Solo respuestas cambiadas hasta esta fecha (incluida), con zona horaria.","schema":{"type":"string"}},{"name":"starting_after","in":"query","required":false,"description":"Id recibido en next_starting_after. Omitir en la primera página. Un id que no sea de este curso devuelve 400.","schema":{"type":"integer"}},{"name":"limit","in":"query","required":false,"description":"Respuestas por página: 1–200; predeterminado 50.","schema":{"type":"integer"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"examples":{"example":{"value":"{\n  \"object\": \"list\",\n  \"data\": [\n    {\n      \"object\": \"feedback_response\",\n      \"id\": 31,\n      \"product_id\": 420,\n      \"survey\": \"course\",\n      \"common_block_id\": 19,\n      \"block_id\": null,\n      \"unit_id\": 71,\n      \"unit_title\": \"Lesson 1\",\n      \"module_id\": 10,\n      \"module_title\": \"Module A\",\n      \"visitor_id\": 88,\n      \"contact_id\": 3,\n      \"visitor_email\": \"student@example.test\",\n      \"visitor_name\": \"Student\",\n      \"created_at\": \"2026-09-27T08:00:00.000Z\",\n      \"updated_at\": \"2026-09-28T09:00:00.000Z\",\n      \"answers\": [\n        {\n          \"question_id\": \"4b6f0d0e-5d1a-4f0e-9a52-2f3c1e7d9a10\",\n          \"question\": \"How was this lesson?\",\n          \"kind\": \"stars\",\n          \"value\": 5\n        },\n        {\n          \"question_id\": \"9c1d2e3f-0a4b-4c5d-8e6f-7a8b9c0d1e2f\",\n          \"question\": \"What would you improve?\",\n          \"kind\": \"text\",\n          \"value\": \"More examples\"\n        }\n      ]\n    }\n  ],\n  \"has_more\": true,\n  \"next_starting_after\": 31\n}"}}}}}}}},"/external-delivery/connections":{"get":{"operationId":"external_connections_list","tags":["Entregas externas por API"],"summary":"Conexiones de entrega externa","description":"Lista conexiones autorizadas y estado de credenciales, sin devolver secretos. Páginas de hasta 100 registros.","security":[{"apiKey":[]}],"parameters":[{"name":"starting_after","in":"query","required":false,"schema":{"type":"integer"}}],"responses":{"200":{"description":"OK"}}},"post":{"operationId":"external_connections_save","tags":["Entregas externas por API"],"summary":"Guardar conexión de entrega externa","description":"POST JSON a una URL HTTPS pública sin parámetros, fragmentos ni credenciales. name (2–120), url (hasta 500), auth (none o bearer), enabled (true por defecto). Sin id crea otra conexión. Con id actualiza nombre, enabled y token, conservando URL, auth y contrato de éxito. token (12–4096) es de solo escritura: omitirlo conserva el actual. Para bearer autorízalo por esta API, nunca dentro del chat. successField y successValue deben indicarse juntos; si faltan, cualquier HTTP 2xx confirma. La API de destino debe soportar reintentos con Idempotency-Key; idempotencySupported debe ser true. No se envía ninguna prueba ni se concede acceso al guardar. Deshabilitar la conexión detiene sus envíos pendientes.","security":[{"apiKey":[]}],"responses":{"200":{"description":"OK","content":{"application/json":{"examples":{"example":{"value":"{\n  \"data\": {\n    \"id\": 7,\n    \"name\": \"External access\",\n    \"url\": \"https://service.example/api/access\",\n    \"auth\": \"bearer\",\n    \"enabled\": true,\n    \"has_credential\": true,\n    \"success_field\": \"ok\",\n    \"success_value\": true\n  }\n}"}}}}}},"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"integer"},"name":{"type":"string"},"url":{"type":"string"},"auth":{"type":"string","enum":["none","bearer"]},"token":{"type":"string"},"enabled":{"type":"boolean"},"successField":{"type":"string"},"successValue":{"anyOf":[{"type":"string"},{"type":"number"},{"type":"boolean"},{"type":"string","nullable":true,"enum":[null]}]},"idempotencySupported":{"enum":[true],"type":"boolean"}},"required":["name","url","auth","idempotencySupported"]}}}}}},"/external-delivery/rules":{"get":{"operationId":"external_rules_list","tags":["Entregas externas por API"],"summary":"Reglas de entrega externa","description":"Lista las asociaciones entre productos y conexiones, sus campos y activación. Páginas de hasta 100 registros.","security":[{"apiKey":[]}],"parameters":[{"name":"starting_after","in":"query","required":false,"schema":{"type":"integer"}}],"responses":{"200":{"description":"OK"}}},"post":{"operationId":"external_rules_save","tags":["Entregas externas por API"],"summary":"Guardar regla de entrega externa","description":"Asocia el producto exacto vendido (el pack, no sus componentes) a una conexión. payload contiene entre 1 y 32 campos JSON planos: cada valor lleva source (buyer.email, buyer.name, purchase.id o product.id) o value (constante de texto, número, booleano o null). Sin id crea o sustituye la regla de ese producto y conexión. Con id solo puede actualizar esa misma asociación. enabled es false por defecto y requiere una conexión habilitada y autorizada. Solo compras futuras registradas; incluye compras externas importadas. No incluye compradores antiguos, etiquetas ni accesos manuales. Cambiar o desactivar una regla no altera entregas ya en cola. refundPolicy es review por defecto: cualquier devolución cancela pendientes y deja entregas enviadas o en curso para revisión manual; keep conserva la entrega. No hay revocación automática.","security":[{"apiKey":[]}],"responses":{"200":{"description":"OK"}},"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"integer"},"productId":{"type":"integer"},"connectionId":{"type":"integer"},"payload":{"type":"object","additionalProperties":{"anyOf":[{"type":"object","properties":{"source":{"type":"string","enum":["buyer.email","buyer.name","purchase.id","product.id"]}},"required":["source"],"additionalProperties":false},{"type":"object","properties":{"value":{"anyOf":[{"type":"string","maxLength":1000},{"type":"number"},{"type":"boolean"},{"type":"string","nullable":true,"enum":[null]}]}},"required":["value"],"additionalProperties":false}]},"minProperties":1,"maxProperties":32},"enabled":{"type":"boolean"},"refundPolicy":{"type":"string","enum":["review","keep"]}},"required":["productId","connectionId","payload"]}}}}}},"/external-delivery/deliveries":{"get":{"operationId":"external_deliveries_list","tags":["Entregas externas por API"],"summary":"Estado de entregas externas","description":"Lista compras y estado: pending, delivering, delivered, failed, review o cancelled. delivered significa confirmación según el contrato de la API; no confirma inicio de sesión del comprador. Hasta ocho intentos automáticos ante fallos temporales, con la misma Idempotency-Key. Páginas de hasta 100 registros.","security":[{"apiKey":[]}],"parameters":[{"name":"starting_after","in":"query","required":false,"schema":{"type":"integer"}}],"responses":{"200":{"description":"OK"}}}},"/external-delivery/deliveries/{id}/retry":{"post":{"operationId":"external_deliveries_retry","tags":["Entregas externas por API"],"summary":"Reintentar entrega externa","description":"Pone una entrega failed en pending, conservando su clave idempotente. Requiere conexión habilitada y ninguna devolución pendiente de revisión. No ejecuta el acceso durante esta petición.","security":[{"apiKey":[]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"integer"}}],"responses":{"200":{"description":"OK"}}}},"/chat":{"post":{"operationId":"chat_send","tags":["Chat con el clon"],"summary":"Hablar con el clon","description":"Envía un mensaje y recibe la respuesta completa del clon en JSON (sin streaming), con sus acciones estructuradas: producto recomendado, enlace, vídeo, oferta de hablar con un humano. Mismo motor que la web y WhatsApp (conocimiento, FAQs, catálogo). Cada respuesta cuenta 1 contra las respuestas mensuales de tu plan, y las conversaciones quedan visibles en el panel en el canal «API». También respeta el límite mensual que configures para ese clon (403 cap_reached) y frena repeticiones automáticas antes de generar una respuesta (429 automated_repetition).","security":[{"apiKey":[]}],"responses":{"200":{"description":"OK","content":{"application/json":{"examples":{"example":{"value":"{\n  \"object\": \"chat.reply\",\n  \"conversation_id\": 412,\n  \"visitor_id\": 88,\n  \"reply\": {\n    \"content\": \"¡Claro! Para empezar con la sentadilla te recomiendo…\",\n    \"actions\": [\n      { \"type\": \"product\", \"product_id\": 172 },\n      { \"type\": \"link\", \"url\": \"https://example.com/guia\", \"label\": \"Ver la guía\" }\n    ]\n  },\n  \"usage\": { \"input_tokens\": 5210, \"output_tokens\": 240 }\n}"}}}}}},"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"message":{"type":"string","description":"El mensaje del usuario (máx 4000 caracteres)."},"conversation_id":{"type":"integer","description":"Continúa ese hilo (te lo devolvió una llamada anterior). Si lo envías, los campos de visitante se ignoran."},"visitor_external_id":{"type":"string","description":"TU id estable del usuario final (recomendado para sistemas). Cada usuario mantiene su hilo continuo."},"visitor_email":{"type":"string","description":"Identifica al usuario por su email (se une al mismo visitante del widget verificado, si existe)."},"visitor_name":{"type":"string"},"version_id":{"type":"integer","description":"Qué clon responde. Omitir = el clon activo."},"new_conversation":{"type":"boolean","description":"true abre un hilo nuevo en vez de continuar el más reciente (p. ej. un ticket distinto)."}},"required":["message"]}}}}}},"/contacts":{"get":{"operationId":"contacts_list","tags":["Contactos y etiquetas"],"summary":"Listar contactos","description":"Exporta tus contactos con sus etiquetas (y la caducidad de cada una).","security":[{"apiKey":[]}],"parameters":[{"name":"limit","in":"query","required":false,"schema":{"type":"integer"}},{"name":"starting_after","in":"query","required":false,"description":"Cursor: devuelve contactos con id mayor que este.","schema":{"type":"integer"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"examples":{"example":{"value":"{\n  \"object\": \"list\",\n  \"data\": [{\n    \"object\": \"contact\",\n    \"id\": 3,\n    \"email\": \"ana@example.com\",\n    \"name\": \"Ana Pérez\",\n    \"phone\": null,\n    \"source\": \"visitor\",\n    \"tags\": [{ \"name\": \"Alumna Curso X\", \"color\": \"blue\", \"expires_at\": null }],\n    \"conversation_count\": 4,\n    \"first_seen_at\": \"2026-05-01T10:00:00.000Z\",\n    \"last_seen_at\": \"2026-07-01T09:30:00.000Z\",\n    \"created_at\": \"2026-05-01T10:00:00.000Z\",\n    \"updated_at\": \"2026-07-01T09:30:00.000Z\"\n  }],\n  \"has_more\": true,\n  \"next_starting_after\": 3\n}"}}}}}}},"post":{"operationId":"contacts_create","tags":["Contactos y etiquetas"],"summary":"Crear o actualizar un contacto","description":"Importa un contacto desde tu sistema. Idempotente por email: si ya existe, enriquece los campos vacíos y suma las etiquetas. Para sincronizar cursos o membresías externas usa el protocolo de Accesos; esta ruta queda para CRM, segmentación e integraciones anteriores.","security":[{"apiKey":[]}],"responses":{"200":{"description":"OK"}},"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"email":{"type":"string"},"name":{"type":"string"},"phone":{"type":"string"},"tags":{"type":"array","items":{"type":"string"},"description":"Etiquetas a añadir (máx. 20; 255 caracteres por nombre). Las que no existan se crean."},"remove_tags":{"type":"array","items":{"type":"string"},"description":"Etiquetas a RETIRAR en esta llamada (máx. 20; 255 caracteres por nombre)."},"expires_at":{"type":"string","nullable":true,"description":"Fecha final de las etiquetas de esta llamada (ISO 8601). Si se omite, conserva las fechas existentes y aplica la duración del producto a sus nuevas etiquetas de acceso. null concede acceso de por vida. Una fecha explícita actualiza el vencimiento."}},"required":["email"]}}}}}},"/contacts/{id}":{"get":{"operationId":"contacts_get","tags":["Contactos y etiquetas"],"summary":"Leer un contacto","description":"Un contacto concreto con sus etiquetas.","security":[{"apiKey":[]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"integer"}}],"responses":{"200":{"description":"OK"}}},"delete":{"operationId":"contacts_delete","tags":["Contactos y etiquetas"],"summary":"Borrar un contacto","description":"Borra el contacto PERMANENTEMENTE, con sus notas y etiquetas. Sus conversaciones NO se borran. Pensado para sincronizar bajas desde tu sistema (RGPD, limpieza del CRM).","security":[{"apiKey":[]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"integer"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"examples":{"example":{"value":"{ \"id\": 3, \"deleted\": true }"}}}}}}}},"/tags":{"get":{"operationId":"tags_list","tags":["Contactos y etiquetas"],"summary":"Listar etiquetas","description":"Tu catálogo de etiquetas con su significado para el clon (clon_context), el producto enganchado y el nº de contactos.","security":[{"apiKey":[]}],"responses":{"200":{"description":"OK","content":{"application/json":{"examples":{"example":{"value":"{\n  \"object\": \"list\",\n  \"data\": [{\n    \"object\": \"tag\",\n    \"id\": 24569,\n    \"name\": \"Alumna Curso X\",\n    \"color\": \"blue\",\n    \"clon_context\": \"Es alumna del Curso X\",\n    \"product_id\": 172,\n    \"contact_count\": 41\n  }],\n  \"has_more\": false,\n  \"next_starting_after\": null\n}"}}}}}}}},"/access/mappings":{"get":{"operationId":"access_mappings_list","tags":["Accesos de academias"],"summary":"Listar relaciones de cursos externos","description":"Devuelve el diccionario que traduce los IDs estables de una academia a tus Products de Clonify.","security":[{"apiKey":[]}],"parameters":[{"name":"provider","in":"query","required":false,"description":"Filtra por plataforma o conector.","schema":{"type":"string"}},{"name":"account_id","in":"query","required":false,"description":"Filtra por la escuela concreta dentro de esa plataforma.","schema":{"type":"string"}}],"responses":{"200":{"description":"OK"}}},"post":{"operationId":"access_mappings_upsert","tags":["Accesos de academias"],"summary":"Relacionar un curso externo","description":"Crea o cambia la relación entre un curso de la academia y un Product de tu cuenta. Es idempotente por plataforma, cuenta e ID externo.","security":[{"apiKey":[]}],"responses":{"200":{"description":"OK"}},"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"provider":{"type":"string","description":"Identificador estable del conector, por ejemplo wordpress o custom_web."},"account_id":{"type":"string","description":"Identificador estable de la escuela o instalación."},"external_product_id":{"type":"string","description":"ID inmutable del curso en la academia; nunca uses el título."},"external_product_name":{"type":"string","description":"Nombre visible, solo para diagnóstico."},"product_id":{"type":"integer","description":"ID del Product correspondiente en Clonify."},"access_url":{"type":"string","description":"URL opcional donde el alumno abre el curso. Debe ser http(s)."}},"required":["provider","account_id","external_product_id","product_id"]}}}}},"delete":{"operationId":"access_mappings_delete","tags":["Accesos de academias"],"summary":"Quitar una relación de curso","description":"Elimina el diccionario; no borra el historial de accesos ya recibido.","security":[{"apiKey":[]}],"parameters":[{"name":"provider","in":"query","required":true,"description":"Plataforma o conector de la relación.","schema":{"type":"string"}},{"name":"account_id","in":"query","required":true,"description":"Escuela concreta de la relación.","schema":{"type":"string"}},{"name":"external_product_id","in":"query","required":true,"description":"ID estable del curso externo.","schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"examples":{"example":{"value":"{ \"deleted\": true }"}}}}}}}},"/access/sync":{"post":{"operationId":"access_sync","tags":["Accesos de academias"],"summary":"Sincronizar los accesos de una persona","description":"Puerta única para WordPress, academias y webs propias. Activa o caduca pases usando IDs estables, une la identidad por email y evita que el Clon vuelva a vender lo que la persona ya posee.","security":[{"apiKey":[]}],"responses":{"200":{"description":"OK","content":{"application/json":{"examples":{"example":{"value":"{\n  \"object\": \"access_sync\",\n  \"provider\": \"wordpress\",\n  \"account_id\": \"academia-principal\",\n  \"mode\": \"snapshot\",\n  \"received\": 2,\n  \"activated\": 2,\n  \"revoked\": 1,\n  \"stale_ignored\": 0,\n  \"unmapped\": [],\n  \"follow_up\": {\n    \"job_id\": 123,\n    \"product_ids\": [42, 43],\n    \"welcome_requested\": true,\n    \"automations_requested\": true,\n    \"status\": \"queued\"\n  }\n}"}}}}}},"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"provider":{"type":"string","description":"Identificador estable del conector."},"account_id":{"type":"string","description":"Identificador estable de la escuela o instalación."},"mode":{"type":"string","enum":["snapshot","patch"],"description":"snapshot = foto completa actual; patch = solo los cambios incluidos."},"observed_at":{"type":"string","description":"Cuándo la plataforma observó este estado. Un evento más antiguo no pisa uno reciente."},"event_id":{"type":"string","description":"Referencia opcional del webhook o entrega para auditoría."},"person":{"type":"object","description":"Objeto con external_user_id y email; name es opcional."},"accesses":{"type":"array","items":{"type":"object"},"description":"Lista de cursos: external_product_id, status active/revoked y, opcionalmente, product_id, nombre, URL, alta y caducidad."},"notify":{"type":"boolean","description":"Por defecto true. Prepara la bienvenida de los productos recién obtenidos; respeta el correo desactivado del producto. false la silencia."},"trigger_automations":{"type":"boolean","description":"Por defecto true. Inicia las secuencias activas por etiqueta de alumno al ganar acceso. Para importar sin correos, enviar false junto con notify=false."}},"required":["provider","account_id","mode","observed_at","person","accesses"]}}}}}},"/conversations":{"get":{"operationId":"conversations_list","tags":["Conversaciones"],"summary":"Listar conversaciones","description":"Las conversaciones reales de tu clon, con los mensajes completos. Las pruebas que haces desde el panel con \"Prueba tu clon\" no salen nunca por aquí ni por los webhooks. Pensado para sincronización incremental: pide solo lo nuevo con updated_since.","security":[{"apiKey":[]}],"parameters":[{"name":"limit","in":"query","required":false,"schema":{"type":"integer"}},{"name":"starting_after","in":"query","required":false,"description":"Cursor: devuelve conversaciones con id mayor que este.","schema":{"type":"integer"}},{"name":"updated_since","in":"query","required":false,"description":"Solo conversaciones con actividad desde esa fecha (una vieja con mensaje nuevo entra).","schema":{"type":"string"}},{"name":"visitor_email","in":"query","required":false,"description":"Solo las de ese visitante identificado (el histórico de una persona).","schema":{"type":"string"}},{"name":"channel_id","in":"query","required":false,"description":"Solo las de esa superficie (web, WhatsApp, Instagram…).","schema":{"type":"integer"}},{"name":"review_status","in":"query","required":false,"description":"pending = solo las que nadie ha repasado aún; reviewed = solo las ya repasadas.","schema":{"type":"string","enum":["pending","reviewed"]}}],"responses":{"200":{"description":"OK","content":{"application/json":{"examples":{"example":{"value":"{\n  \"object\": \"list\",\n  \"data\": [{\n    \"object\": \"conversation\",\n    \"id\": 90,\n    \"visitor\": { \"email\": \"ana@example.com\", \"name\": \"Ana Pérez\" },\n    \"channel_id\": 15,\n    \"title\": null,\n    \"first_page_url\": null,\n    \"review_status\": \"pending\",\n    \"message_count\": 4,\n    \"started_at\": \"2026-05-15T09:36:48.918Z\",\n    \"last_message_at\": \"2026-05-15T09:37:16.909Z\",\n    \"messages\": [\n      { \"role\": \"user\", \"content\": \"hola, ¿me ayudas?\", \"created_at\": \"…\" },\n      { \"role\": \"assistant\", \"content\": \"¡Claro! Cuéntame…\", \"created_at\": \"…\" }\n    ]\n  }],\n  \"has_more\": true,\n  \"next_starting_after\": 95\n}"}}}}}}}},"/conversations/{id}":{"get":{"operationId":"conversations_get","tags":["Conversaciones"],"summary":"Leer una conversación","description":"Una conversación con sus mensajes, por id.","security":[{"apiKey":[]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"integer"}}],"responses":{"200":{"description":"OK"}}}},"/conversations/reviewed":{"post":{"operationId":"conversations_reviewed","tags":["Conversaciones"],"summary":"Marcar conversaciones como revisadas","description":"Deja constancia de qué conversaciones ya has procesado, para no volver a traértelas. Es la pareja de review_status=pending al listar: pide lo pendiente, trátalo y márcalo. Un id que no existe no tumba el lote: sale en not_found.","security":[{"apiKey":[]}],"responses":{"200":{"description":"OK","content":{"application/json":{"examples":{"example":{"value":"{\n  \"object\": \"conversations_reviewed\",\n  \"review_status\": \"reviewed\",\n  \"updated\": [90, 91],\n  \"unchanged\": [88],\n  \"not_found\": [4001]\n}"}}}}}},"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"conversation_ids":{"type":"array","items":{"type":"integer"},"description":"Ids a marcar, de 1 a 100 por petición."},"reviewed":{"type":"boolean","description":"true (por defecto) marca como revisadas; false las devuelve a pendientes."}},"required":["conversation_ids"]}}}}}},"/visitors/{anon_id}":{"delete":{"operationId":"visitors_delete","tags":["Conversaciones"],"summary":"Borrar un visitante y todo su historial","description":"Borra al visitante ENTERO por su identificador anónimo: su ficha de visitante y, en cascada, sus conversaciones, mensajes, valoraciones, progreso de cursos, adjuntos y los casos de escalación que abrió (los avisos que llegaron a tu bandeja, con su transcripción). Es el derecho de supresión de un usuario final ejecutado desde tu sistema, pensado para el clon embebido con identidad anónima propia (el aid que pasas al panel). Devuelve un acuse con lo borrado y la hora de ejecución. Idempotente: un segundo borrado del mismo anon_id responde 404 not_found. No confundir con DELETE /contacts/{id}, que borra la ficha de CRM y conserva las conversaciones.","security":[{"apiKey":[]}],"parameters":[{"name":"anon_id","in":"path","required":true,"description":"Identificador anónimo del visitante: el aid que pasas al panel embebido o el que generó el widget. Único por cuenta. Entre 8 y 128 caracteres (letras, números, punto, guion, guion bajo, dos puntos o arroba).","schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"examples":{"example":{"value":"{\n  \"object\": \"visitor_erasure\",\n  \"anon_id\": \"3f9c0a1b2c3d4e5f6a7b8c9d0e1f2a3b\",\n  \"visitor_id\": 8123,\n  \"deleted\": true,\n  \"conversations_deleted\": 3,\n  \"messages_deleted\": 41,\n  \"support_cases_deleted\": 1,\n  \"erased_at\": \"2026-09-09T15:02:11.000Z\"\n}"}}}}}}}},"/products":{"get":{"operationId":"products_list","tags":["Catálogo de productos"],"summary":"Listar productos","description":"Tu catálogo (la ficha comercial de cada producto). Por defecto solo los activos.","security":[{"apiKey":[]}],"parameters":[{"name":"kind","in":"query","required":false,"description":"Filtra por tipo (course, masterclass, live_event…).","schema":{"type":"string"}},{"name":"publish_status","in":"query","required":false,"schema":{"type":"string","enum":["published","draft","archived"]}},{"name":"include_inactive","in":"query","required":false,"description":"true para incluir también los pausados.","schema":{"type":"boolean"}}],"responses":{"200":{"description":"OK"}}},"post":{"operationId":"products_create","tags":["Catálogo de productos"],"summary":"Crear un producto","description":"Crea una ficha en el catálogo del clon. Queda activa: el clon podrá recomendarla cuando encaje en la conversación.","security":[{"apiKey":[]}],"responses":{"201":{"description":"OK"}},"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"title":{"type":"string"},"description":{"type":"string"},"price":{"type":"number","description":"Omitido o null = «consultar con el creador»."},"currency":{"type":"string","description":"ISO de 3 letras. Por defecto EUR."},"checkout_url":{"type":"string","nullable":true},"tags":{"type":"array","items":{"type":"string"},"description":"Tipo(s) del producto: curso, masterclass, libro, comunidad, coaching… Al menos uno."}},"required":["title","description","tags"]}}}}}},"/products/{id}":{"get":{"operationId":"products_get","tags":["Catálogo de productos"],"summary":"Leer un producto","description":"La ficha completa de un producto, con sus FAQs incluidas.","security":[{"apiKey":[]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"integer"}}],"responses":{"200":{"description":"OK"}}},"patch":{"operationId":"products_update","tags":["Catálogo de productos"],"summary":"Editar un producto","description":"Solo se tocan los campos que envíes. Cambiar título, descripción o tipos re-indexa el producto para el matching del clon.","security":[{"apiKey":[]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"integer"}}],"responses":{"200":{"description":"OK"}},"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"replay_visibility":{"type":"string","enum":["all","since_joined"],"description":"Histórico disponible en cursos, Actualizaciones y repeticiones de directos. all permite todo el histórico; since_joined solo el contenido desde la entrada. Las renovaciones conservan esa fecha; al terminar el acceso se cierra el contenido y una nueva suscripción comienza otra ventana. Omitirlo conserva el ajuste actual. GET devuelve replay_visibility."},"title":{"type":"string"},"description":{"type":"string"},"price":{"type":"number","description":"null = «consultar con el creador»."},"currency":{"type":"string"},"checkout_url":{"type":"string","nullable":true},"cta_label":{"type":"string","nullable":true,"description":"Texto del botón de la tarjeta. null = botón genérico."},"tags":{"type":"array","items":{"type":"string"}},"is_active":{"type":"boolean","description":"false pausa el producto comercialmente (el clon deja de recomendarlo)."},"publish_status":{"type":"string","enum":["published","draft","archived"]}},"required":[]}}}}},"delete":{"operationId":"products_delete","tags":["Catálogo de productos"],"summary":"Borrar un producto","description":"Borra el producto PERMANENTEMENTE, con sus FAQs. Para dejar de recomendarlo sin perderlo, mejor PATCH con is_active=false (pausa) o publish_status='archived'.","security":[{"apiKey":[]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"integer"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"examples":{"example":{"value":"{ \"id\": 172, \"deleted\": true }"}}}}}}}},"/products/{id}/entries/import":{"post":{"operationId":"product_entries_import","tags":["Catálogo de productos"],"summary":"Importar entradas de contenido","description":"Añade de 1 a 50 entradas de texto a un producto existente (course, masterclass, capsules o downloadable), en una única operación. No crea productos ni cambia precios, accesos o el estado del producto, y no envía avisos. HTML saneado, máximo 50.000 caracteres por entrada y 3 MB por petición. Cada entrada queda en borrador salvo status=published. published_at conserva la fecha histórica; no programa la publicación. Una repetición idéntica devuelve 200 y replayed=true; la primera importación devuelve 201. Reutilizar external_id con otro contenido devuelve 409: esta operación no sincroniza correcciones.","security":[{"apiKey":[]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"integer"}}],"responses":{"201":{"description":"OK","content":{"application/json":{"examples":{"example":{"value":"{ \"data\": { \"product_id\": 172, \"external_id\": \"edition-2026-09-11\", \"module_id\": 80, \"entries\": [{ \"external_id\": \"article-1\", \"unit_id\": 501, \"status\": \"draft\" }], \"replayed\": false } }"}}}}}},"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"external_id":{"type":"string","description":"Identificador estable del lote dentro de este producto. Reutilízalo al reintentar."},"collection_title":{"type":"string","description":"Título opcional de un módulo nuevo que agrupa estas entradas."},"entries":{"type":"array","minItems":1,"maxItems":50,"items":{"type":"object","additionalProperties":false,"required":["external_id","title","html"],"properties":{"external_id":{"type":"string","minLength":1,"maxLength":200},"title":{"type":"string","minLength":1,"maxLength":200},"description":{"type":"string","maxLength":2000},"category":{"type":"string","minLength":1,"maxLength":80},"summary_html":{"type":"string","maxLength":8000},"html":{"type":"string","minLength":1,"maxLength":50000},"published_at":{"type":"string","format":"date-time"},"status":{"type":"string","enum":["draft","published"],"default":"draft"}}},"description":"Entradas ordenadas con external_id único dentro del lote, title y html obligatorios; description, published_at (ISO 8601 con zona horaria) y status (draft por defecto o published) opcionales. Conserva exactamente el mismo contenido al reintentar. category (hasta 80 caracteres) y summary_html (hasta 8.000 caracteres, solo negrita y cursiva) son opcionales. Las etiquetas b/i se convierten a strong/em; se eliminan enlaces, estilos y contenido ejecutable."}},"required":["external_id","entries"]}}}}},"get":{"operationId":"product_entries_import_get","tags":["Catálogo de productos"],"summary":"Consultar una importación","description":"Devuelve el recibo original con los identificadores de las entradas creadas. Solo consulta importaciones de la cuenta de la llave. 404 si no existe. No refleja modificaciones posteriores de las entradas.","security":[{"apiKey":[]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"integer"}},{"name":"external_id","in":"query","required":true,"description":"Identificador del lote utilizado al importar.","schema":{"type":"string"}}],"responses":{"200":{"description":"OK"}}},"patch":{"operationId":"product_entries_presentation","tags":["Catálogo de productos"],"summary":"Actualizar la presentación de entradas","description":"Actualiza únicamente categoría y resumen de entradas ya importadas. Conserva cuerpo, fechas, estado, accesos y recibo original. Operación atómica, repetible y limitada a la cuenta y producto de la llave. null elimina un campo; omitirlo lo conserva.","security":[{"apiKey":[]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"integer"}}],"responses":{"200":{"description":"OK"}},"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"external_id":{"type":"string","description":"Identificador del lote original."},"entries":{"type":"array","minItems":1,"maxItems":50,"items":{"type":"object","additionalProperties":false,"required":["external_id"],"anyOf":[{"required":["category"]},{"required":["summary_html"]}],"properties":{"external_id":{"type":"string","minLength":1,"maxLength":200},"category":{"type":["string","null"],"minLength":1,"maxLength":80},"summary_html":{"type":["string","null"],"maxLength":8000}}},"description":"De 1 a 50 entradas con external_id original y category o summary_html. No se permiten entradas repetidas."}},"required":["external_id","entries"]}}}}}},"/products/{id}/faqs":{"get":{"operationId":"product_faqs_list","tags":["Catálogo de productos"],"summary":"Listar FAQs de un producto","description":"Las respuestas que enseñan al clon a resolver dudas y objeciones sobre ese producto.","security":[{"apiKey":[]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"integer"}}],"responses":{"200":{"description":"OK"}}},"post":{"operationId":"product_faqs_create","tags":["Catálogo de productos"],"summary":"Crear FAQ de producto","description":"Cuando pregunten algo parecido SOBRE ese producto, el clon responderá lo indicado.","security":[{"apiKey":[]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"integer"}}],"responses":{"201":{"description":"OK"}},"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"question":{"type":"string"},"answer":{"type":"string"},"answer_mode":{"type":"string","enum":["free","contexto"],"description":"Por defecto 'free': el clon responde con su voz apoyándose en tu texto (la FAQ es guía). 'contexto' lo fija con más fidelidad (enlaces, pasos y datos se respetan)."},"actions":{"type":"array","items":{"type":"object"},"description":"Acciones al dispararse: {type: asset|product|external_link|video_embed|escalate_offer, …}."}},"required":["question","answer"]}}}}}},"/products/{id}/faqs/{faqId}":{"delete":{"operationId":"product_faqs_delete","tags":["Catálogo de productos"],"summary":"Borrar FAQ de producto","description":"Borra la FAQ PERMANENTEMENTE. No hay papelera: si fue un error, vuelve a crearla.","security":[{"apiKey":[]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"integer"}},{"name":"faqId","in":"path","required":true,"schema":{"type":"integer"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"examples":{"example":{"value":"{ \"id\": 51, \"product_id\": 172, \"deleted\": true }"}}}}}}}},"/sales":{"post":{"operationId":"sales_create","tags":["Ventas externas"],"summary":"Registrar una venta cobrada fuera","description":"Si cobras fuera de Clonify (tu pasarela, Hotmart, ThriveCart…), registra aquí cada venta y el programa de afiliados funciona igual que si el cobro fuera nuestro: la venta se atribuye por el email del comprador (el lead manda) o por el código ref del afiliado si viajó hasta tu checkout, y la comisión se calcula con tu % y tus plazos. El afiliado la ve en su portal al momento. Idempotente por order_ref: reenviar la misma venta responde already_recorded y no duplica nada — puedes reintentar sin miedo. Pensado para dispararse desde n8n, Make o Zapier al cerrarse una venta.","security":[{"apiKey":[]}],"responses":{"200":{"description":"OK","content":{"application/json":{"examples":{"example":{"value":"{\n  \"object\": \"sale\",\n  \"order_ref\": \"PED-1042\",\n  \"product_id\": 12,\n  \"product_title\": \"Máster Ejecutivo\",\n  \"amount_cents\": 199700,\n  \"already_recorded\": false,\n  \"affiliate\": { \"name\": \"Ana Ruiz\", \"code\": \"a7fk29\" }\n}"}}}}}},"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"email":{"type":"string","description":"Email del comprador. Es lo que une la venta con su lead — y con el afiliado que lo trajo."},"amount_cents":{"type":"integer","description":"Importe cobrado, en céntimos (1.997 € → 199700)."},"order_ref":{"type":"string","description":"El nº de pedido en tu sistema: la identidad de la venta. Reenviarlo no duplica, y la devolución se registra contra él."},"product_id":{"type":"integer","description":"La ficha de Clonify del producto vendido (GET /api/v1/products para los ids). Si vendes fuera, vale la ficha de enlace externo."},"ref":{"type":"string","description":"Código de afiliado (?ref=) arrastrado hasta tu checkout, si lo tienes. Es el seguro por si el comprador paga con otro email."},"currency":{"type":"string","description":"Hoy solo EUR (\"eur\" u omitido). Con otra moneda la puerta devuelve 400: convierte a céntimos de euro antes de mandar."},"name":{"type":"string"}},"required":["email","amount_cents","order_ref","product_id"]}}}}}},"/sales/refund":{"post":{"operationId":"sales_refund","tags":["Ventas externas"],"summary":"Registrar una devolución","description":"Refleja la devolución de una venta registrada por la API. La comisión del afiliado baja sola (siempre se calcula neta de devoluciones); la venta no se borra, queda marcada para que todo cuadre con tu pasarela.","security":[{"apiKey":[]}],"responses":{"200":{"description":"OK","content":{"application/json":{"examples":{"example":{"value":"{\n  \"object\": \"refund\",\n  \"order_ref\": \"PED-1042\",\n  \"refunded_amount_cents\": 199700\n}"}}}}}},"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"order_ref":{"type":"string","description":"El mismo nº de pedido con el que registraste la venta."},"refunded_amount_cents":{"type":"integer","description":"El ACUMULADO devuelto de esa venta, en céntimos — no el delta. Mandar dos veces lo mismo deja lo mismo, y el acumulado SOLO PUEDE CRECER: una devolución registrada no se puede deshacer, así que verifica el order_ref antes de mandar. Devolución total = el importe entero."}},"required":["order_ref","refunded_amount_cents"]}}}}}},"/brain/sources":{"get":{"operationId":"sources_list","tags":["Conocimiento"],"summary":"Listar fuentes del conocimiento","description":"El índice del conocimiento del clon (título, tipo, palabras, estado). El texto completo lo da el detalle.","security":[{"apiKey":[]}],"parameters":[{"name":"limit","in":"query","required":false,"schema":{"type":"integer"}},{"name":"starting_after","in":"query","required":false,"description":"Cursor: devuelve fuentes con id mayor que este.","schema":{"type":"integer"}},{"name":"source_type","in":"query","required":false,"schema":{"type":"string","enum":["youtube","text","pdf","video","website","instagram","vimeo"]}},{"name":"status","in":"query","required":false,"schema":{"type":"string","enum":["pending","extracting","processing","ready","error"]}}],"responses":{"200":{"description":"OK"}}},"post":{"operationId":"sources_create","tags":["Conocimiento"],"summary":"Entrenar al clon con texto","description":"Crea una fuente de texto, la trocea, la embebe y la enlaza a todos tus clones. Igual que subirla por el panel. Consume cuota de palabras del plan (403 words_quota_exceeded si no queda).","security":[{"apiKey":[]}],"responses":{"201":{"description":"OK","content":{"application/json":{"examples":{"example":{"value":"{\n  \"object\": \"brain_source\",\n  \"id\": 12438,\n  \"source_type\": \"text\",\n  \"title\": \"Guía de sentadilla\",\n  \"word_count\": 210,\n  \"status\": \"ready\",\n  \"chunk_count\": 3,\n  \"replaced\": false,\n  \"created_at\": \"2026-07-03T17:00:00.000Z\",\n  \"updated_at\": \"2026-07-03T17:00:04.000Z\"\n}"}}}}}},"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"title":{"type":"string"},"content":{"type":"string"},"content_date":{"type":"string","description":"Fecha del contenido original (no de la subida). Omitir = hoy."},"external_id":{"type":"string","description":"TU identificador estable del contenido. Si ya subiste uno con este id, se REEMPLAZA (upsert idempotente para sincronizar). La respuesta lleva replaced: true."},"content_hash":{"type":"string","description":"Tu huella del contenido original. Te la devolvemos en los listados para detectar qué cambió y resubir solo eso."},"folder_id":{"type":"integer"}},"required":["title","content"]}}}}},"delete":{"operationId":"sources_delete_by_external","tags":["Conocimiento"],"summary":"Borrar una fuente por tu id","description":"Igual que el borrado por id numérico, pero usando TU external_id (el que pasaste al crearla): tu sincronización no necesita conocer nuestros ids.","security":[{"apiKey":[]}],"parameters":[{"name":"external_id","in":"query","required":true,"description":"Tu identificador estable de la fuente.","schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"examples":{"example":{"value":"{ \"id\": 12438, \"external_id\": \"notion-doc-42\", \"deleted\": true }"}}}}}}}},"/brain/sources/link":{"post":{"operationId":"sources_link_create","tags":["Conocimiento"],"summary":"Entrenar al clon con un enlace","description":"Una página web (se extrae su contenido limpio) o un vídeo de YouTube donde habla SOLO el creador (se usa su transcripción). Mismo pipeline y cuota de palabras que el texto. Un vídeo con más voces necesita el archivo original en el panel: 422 youtube_needs_file.","security":[{"apiKey":[]}],"responses":{"201":{"description":"OK","content":{"application/json":{"examples":{"example":{"value":"{\n  \"object\": \"brain_source\",\n  \"id\": 12440,\n  \"source_type\": \"website\",\n  \"title\": \"Guía completa de la sentadilla\",\n  \"word_count\": 1480,\n  \"status\": \"ready\",\n  \"chunk_count\": 9,\n  \"replaced\": false,\n  \"created_at\": \"2026-07-05T10:00:00.000Z\",\n  \"updated_at\": \"2026-07-05T10:00:21.000Z\"\n}"}}}}}},"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"url":{"type":"string","description":"URL de la página web o del vídeo de YouTube."},"has_other_speakers":{"type":"boolean","description":"Solo vídeos: true si aparece otra persona además del creador (entrevista, podcast)."},"external_id":{"type":"string","description":"TU identificador estable del contenido. Si ya subiste uno con este id, se REEMPLAZA (upsert idempotente). La extracción va ANTES del reemplazo: un enlace roto no destruye la fuente anterior."},"content_hash":{"type":"string","description":"Tu huella del contenido original. Te la devolvemos en los listados para detectar qué cambió."},"folder_id":{"type":"integer"}},"required":["url"]}}}}}},"/brain/sources/pdf":{"post":{"operationId":"sources_pdf_create","tags":["Conocimiento"],"summary":"Entrenar al clon con un PDF","description":"Los bytes del PDF en base64, hasta ~3MB. Solo PDFs con texto seleccionable: uno escaneado (solo imágenes) devuelve 422 pdf_no_text. Pásalo por OCR o envía el texto con POST /brain/sources. Misma cuota de palabras que el texto.","security":[{"apiKey":[]}],"responses":{"201":{"description":"OK"}},"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"title":{"type":"string"},"file_base64":{"type":"string","description":"Los bytes del PDF codificados en base64 (sin prefijo data:). Límite ~3MB de PDF. Para más grande, el panel."},"content_date":{"type":"string","description":"Fecha del contenido. Omitir = la fecha interna del PDF si la trae. Si no, hoy."},"external_id":{"type":"string","description":"TU identificador estable (p. ej. la ruta del archivo). Si ya subiste uno con este id, se REEMPLAZA (upsert idempotente)."},"content_hash":{"type":"string"},"folder_id":{"type":"integer"}},"required":["title","file_base64"]}}}}}},"/brain/sources/{id}":{"get":{"operationId":"sources_get","tags":["Conocimiento"],"summary":"Leer una fuente","description":"Una fuente con su texto completo (content_text). Puede pesar: el listado no lo arrastra.","security":[{"apiKey":[]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"integer"}}],"responses":{"200":{"description":"OK"}}},"delete":{"operationId":"sources_delete","tags":["Conocimiento"],"summary":"Borrar una fuente","description":"Borra la fuente PERMANENTEMENTE, con sus trozos. LIBERA cuota de palabras. Es la pieza del patrón «quitar y resubir» para sincronizar contenido. No hay papelera.","security":[{"apiKey":[]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"integer"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"examples":{"example":{"value":"{ \"id\": 12438, \"deleted\": true }"}}}}}}}},"/brain/folders":{"get":{"operationId":"folders_list","tags":["Conocimiento"],"summary":"Listar carpetas del conocimiento","description":"Las carpetas donde se organiza el material del clon, con su id. Es lo que necesitas para pasar folder_id al subir una fuente. Lista plana: cada carpeta dice de cuál cuelga en parent_id, y el árbol lo montas tú. Vienen todas, sin paginar.","security":[{"apiKey":[]}],"responses":{"200":{"description":"OK","content":{"application/json":{"examples":{"example":{"value":"{\n  \"object\": \"list\",\n  \"data\": [{\n    \"object\": \"brain_folder\",\n    \"id\": 88,\n    \"name\": \"Clases del curso\",\n    \"description\": null,\n    \"parent_id\": null,\n    \"product_id\": 172,\n    \"is_course\": true,\n    \"created_at\": \"2026-05-02T08:12:00.000Z\",\n    \"updated_at\": \"2026-07-19T11:40:00.000Z\"\n  }],\n  \"has_more\": false,\n  \"next_starting_after\": null\n}"}}}}}}},"post":{"operationId":"folders_create","tags":["Conocimiento"],"summary":"Crear una carpeta","description":"Crea una carpeta para archivar el material que subas. IDEMPOTENTE: si ya existe una con ese nombre en el mismo nivel, te devuelve la que hay (created: false) en vez de duplicarla, así que una sincronización que se ejecute cada noche no te llena el conocimiento de copias.","security":[{"apiKey":[]}],"responses":{"201":{"description":"OK","content":{"application/json":{"examples":{"example":{"value":"{\n  \"object\": \"brain_folder\",\n  \"id\": 91,\n  \"name\": \"Newsletter 2026\",\n  \"description\": null,\n  \"parent_id\": null,\n  \"product_id\": null,\n  \"is_course\": false,\n  \"created_at\": \"2026-07-30T10:00:00.000Z\",\n  \"updated_at\": \"2026-07-30T10:00:00.000Z\",\n  \"created\": true\n}"}}}}}},"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string"},"parent_id":{"type":"integer","description":"Carpeta de la que cuelga. Omitir = carpeta de primer nivel."},"description":{"type":"string"}},"required":["name"]}}}}}},"/brain/guided-answers":{"get":{"operationId":"guided_answers_list","tags":["Conocimiento"],"summary":"Listar respuestas","description":"Las preguntas frecuentes de tu Conocimiento (primero las fijadas).","security":[{"apiKey":[]}],"parameters":[{"name":"include_archived","in":"query","required":false,"schema":{"type":"boolean"}}],"responses":{"200":{"description":"OK"}}},"post":{"operationId":"guided_answers_create","tags":["Conocimiento"],"summary":"Crear respuesta","description":"Dos modos. CON TEXTO (con answer): el clon se apoya en ese texto con fidelidad cuando la pregunta encaje, dicho con su voz y leyendo la conversación. LIBRE (sin answer): el clon habla libre y se disparan las actions declaradas.","security":[{"apiKey":[]}],"responses":{"201":{"description":"OK"}},"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"question":{"type":"string","description":"UNA pregunta natural, como la escribiría un visitante (sin amontonar varias con «/»)."},"answer":{"type":"string","description":"La respuesta en la que se apoya el clon. Vacío = modo libre (entonces actions es obligatorio)."},"variants":{"type":"array","items":{"type":"string"},"description":"Otras formas de preguntar LO MISMO. Cada una amplía el match por significado."},"actions":{"type":"array","items":{"type":"object"},"description":"Acciones al dispararse: {type: asset|product|external_link|video_embed|escalate_offer, …}."}},"required":["question"]}}}}}},"/brain/guided-answers/{id}":{"delete":{"operationId":"guided_answers_delete","tags":["Conocimiento"],"summary":"Borrar respuesta","description":"Borra la respuesta PERMANENTEMENTE, con sus variantes. No hay papelera.","security":[{"apiKey":[]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"integer"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"examples":{"example":{"value":"{ \"id\": 204, \"deleted\": true }"}}}}}}}},"/assets":{"get":{"operationId":"assets_list","tags":["Multimedia"],"summary":"Listar Multimedia","description":"Tus archivos (vídeos, audios, imágenes, documentos) con su id, tipo y estado. Los punteros de entrega no vienen aquí porque caducan: pídelos de uno en uno con la ficha del asset. Lo que sí traen las imágenes, audios y archivos ya listos es public_url, la dirección estable bajo tu dominio para usarla tal cual en el HTML de tu web.","security":[{"apiKey":[]}],"parameters":[{"name":"limit","in":"query","required":false,"schema":{"type":"integer"}},{"name":"starting_after","in":"query","required":false,"description":"Cursor: devuelve archivos con id mayor que este.","schema":{"type":"integer"}},{"name":"kind","in":"query","required":false,"description":"Filtra por tipo de archivo.","schema":{"type":"string","enum":["video","audio","image","file"]}}],"responses":{"200":{"description":"OK","content":{"application/json":{"examples":{"example":{"value":"{\n  \"object\": \"list\",\n  \"data\": [{\n    \"object\": \"asset\",\n    \"id\": 42,\n    \"kind\": \"image\",\n    \"title\": \"Portada del curso\",\n    \"file_name\": \"portada.webp\",\n    \"mime_type\": \"image/webp\",\n    \"file_size_bytes\": 48210,\n    \"duration_seconds\": null,\n    \"status\": \"ready\",\n    \"folder_id\": null,\n    \"public_url\": \"https://example.com/media/42\",\n    \"created_at\": \"2026-06-11T09:00:00.000Z\",\n    \"updated_at\": \"2026-06-11T09:00:00.000Z\"\n  }],\n  \"has_more\": false,\n  \"next_starting_after\": null\n}"}}}}}}},"post":{"operationId":"assets_create","tags":["Multimedia"],"summary":"Subir un archivo a Multimedia","description":"Sube un archivo a tu Multimedia. Manda url (un enlace) o file_base64 (los bytes), uno de los dos. El VÍDEO solo por url, sin límite de tamaño: lo descarga nuestro motor y queda en processing unos minutos hasta que está listo. Lo demás queda disponible al instante, y las imágenes se optimizan a WebP por el camino. Ocupa espacio de almacenamiento de tu plan: sin sitio, 403.","security":[{"apiKey":[]}],"responses":{"201":{"description":"OK","content":{"application/json":{"examples":{"example":{"value":"{\n  \"object\": \"asset\",\n  \"id\": 118,\n  \"kind\": \"video\",\n  \"title\": \"Clase 1\",\n  \"file_name\": \"clase-1.mp4\",\n  \"status\": \"processing\",\n  \"public_url\": null\n}"}}}}}},"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"url":{"type":"string","description":"Enlace http(s) accesible al archivo. La vía buena, y la única para vídeo."},"file_base64":{"type":"string","description":"Los bytes en base64, sin el prefijo «data:». Solo archivos pequeños (unos 3 MB): el resto, por url."},"file_name":{"type":"string","description":"Nombre con su extensión. Obligatorio con file_base64; con url se deduce del enlace."},"title":{"type":"string"},"kind":{"type":"string","enum":["video","audio","image","file"]},"folder_id":{"type":"integer"}},"required":[]}}}}}},"/assets/{id}":{"get":{"operationId":"assets_get","tags":["Multimedia"],"summary":"Leer un asset de Multimedia","description":"La ficha de un archivo de tu Multimedia y cómo entregarlo: los vídeos traen embed_url (iframe firmado, listo para incrustar) y el resto (imagen, audio, archivo) trae download_url. Úsalo para resolver las acciones { type: \"asset\", asset_id } que devuelve el chat, igual que resuelves los productos con su ficha. Solo se sirven assets ya procesados. Uno en proceso da 404.","security":[{"apiKey":[]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"integer"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"examples":{"example":{"value":"{\n  \"object\": \"asset\",\n  \"id\": 42,\n  \"kind\": \"file\",\n  \"title\": \"Plantilla de avatar de cliente\",\n  \"file_name\": \"plantilla-avatar.pdf\",\n  \"mime_type\": \"application/pdf\",\n  \"file_size_bytes\": 183422,\n  \"duration_seconds\": null,\n  \"embed_url\": null,\n  \"download_url\": \"https://www.clonify.com/api/v1/assets/42/download\"\n}"}}}}}}}},"/assets/{id}/download":{"get":{"operationId":"assets_download","tags":["Multimedia"],"summary":"Descargar los bytes de un asset","description":"Los bytes del archivo (imagen, audio o documento), con la misma llave que el resto de la API: NO es un enlace público para reenviar a un usuario final. Tu sistema descarga el archivo y lo re-entrega en su plataforma (adjunto de Discord, media de Telegram…). Admite la cabecera Range. Los vídeos no se descargan: se incrustan con el embed_url de la ficha.","security":[{"apiKey":[]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"integer"}}],"responses":{"200":{"description":"OK"}}}},"/versions":{"get":{"operationId":"versions_list","tags":["Clones (versions en la API)"],"summary":"Listar clones","description":"Tus clones y cuál está activo.","security":[{"apiKey":[]}],"responses":{"200":{"description":"OK"}}},"post":{"operationId":"versions_create","tags":["Clones (versions en la API)"],"summary":"Crear un clon","description":"Un clon nuevo lee de tu Conocimiento y de Tu negocio, con su propia configuración. NO lo publica: activarlo sigue siendo un gesto consciente del panel.","security":[{"apiKey":[]}],"responses":{"201":{"description":"OK"}},"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"label":{"type":"string"},"description":{"type":"string"},"copy_from_version_id":{"type":"integer","description":"Copia la configuración de otro clon (capacidades, exclusiones, sugerencias) en vez de empezar en blanco."}},"required":[]}}}}}},"/versions/{id}":{"patch":{"operationId":"versions_update","tags":["Clones (versions en la API)"],"summary":"Configurar un clon","description":"Todos los campos opcionales: solo se aplica lo que envíes. El conocimiento va por EXCLUSIÓN sobre «hereda todo»: por defecto el clon ve todo el conocimiento y tú ocultas carpetas o fuentes.","security":[{"apiKey":[]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"integer"}}],"responses":{"200":{"description":"OK"}},"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"label":{"type":"string","description":"Nuevo nombre. '' lo borra."},"description":{"type":"string"},"avatar_url":{"type":"string"},"silos":{"type":"object","description":"Capacidades on/off: {aprender?: bool, soporte?: bool, recomendar?: bool}. recomendar es «Puede recomendar tus productos»: un clon de Venta lo tiene siempre y uno de Entregables nunca (409 recommend_fixed_by_job si pides lo contrario)."},"accept_images":{"type":"boolean"},"image_rules":{"type":"array","items":{"type":"object"},"description":"Reglas de visión «cuando veas X → interprétalo así»: [{trigger, meaning}]. Reemplaza la lista entera."},"voice_call":{"type":"boolean","description":"Botón de llamada de voz. Requiere una voz configurada (409 voice_not_configured si no)."},"exclude_folder_ids":{"type":"array","items":{"type":"integer"},"description":"Carpetas del conocimiento que este clon NO debe ver."},"include_folder_ids":{"type":"array","items":{"type":"integer"}},"exclude_source_ids":{"type":"array","items":{"type":"integer"}},"include_source_ids":{"type":"array","items":{"type":"integer"}},"exclude_unfoldered_sources":{"type":"boolean"},"suggestions":{"type":"array","items":{"type":"string"},"description":"Chips del chat al abrir. Reemplaza la lista. [] = sin chips."},"cite_source_links":{"type":"boolean"}},"required":[]}}}}}},"/meetings":{"get":{"operationId":"meetings_list","tags":["Reuniones y reservas"],"summary":"Listar reuniones grabadas","description":"Tus reuniones grabadas (más recientes primero): título, fecha, participantes y si la transcripción está lista.","security":[{"apiKey":[]}],"parameters":[{"name":"limit","in":"query","required":false,"schema":{"type":"integer"}}],"responses":{"200":{"description":"OK"}}}},"/meetings/{id}/transcript":{"get":{"operationId":"meetings_transcript","tags":["Reuniones y reservas"],"summary":"Leer transcripción de reunión","description":"La transcripción completa de una reunión, con los hablantes resueltos a nombre real. Si aún se está transcribiendo: 409 transcript_not_ready (reintenta más tarde).","security":[{"apiKey":[]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"integer"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"examples":{"example":{"value":"{\n  \"object\": \"meeting_transcript\",\n  \"meeting_id\": 141,\n  \"title\": \"Sesión con Ana · Consultoría\",\n  \"started_at\": \"2026-07-02T16:01:04.317Z\",\n  \"ended_at\": \"2026-07-02T16:31:12.000Z\",\n  \"participants\": [{ \"name\": \"Marta\", \"role\": \"host\" }, { \"name\": \"Ana\", \"role\": \"guest\" }],\n  \"turns\": [\n    { \"start_ms\": 1200, \"end_ms\": 4100, \"speaker\": \"Marta\", \"text\": \"¿Qué tal la semana?\" },\n    { \"start_ms\": 4500, \"end_ms\": 9800, \"speaker\": \"Ana\", \"text\": \"Cumplí 3 de 4 entrenos…\" }\n  ]\n}"}}}}}}}},"/bookings":{"get":{"operationId":"bookings_list","tags":["Reuniones y reservas"],"summary":"Listar reservas","description":"Las reservas de reunión de toda la cuenta: tipo, fecha, cliente y estado.","security":[{"apiKey":[]}],"parameters":[{"name":"limit","in":"query","required":false,"schema":{"type":"integer"}},{"name":"starting_after","in":"query","required":false,"description":"Cursor: devuelve reservas con id mayor que este.","schema":{"type":"integer"}},{"name":"status","in":"query","required":false,"schema":{"type":"string","enum":["confirmed","cancelled","completed","no_show"]}},{"name":"from","in":"query","required":false,"description":"Solo reservas que empiezan en/después de esa fecha.","schema":{"type":"string"}}],"responses":{"200":{"description":"OK"}}}},"/webinars":{"get":{"operationId":"webinars_list","tags":["Webinars"],"summary":"Listar webinars","description":"Tus webinars con su nº de inscritos. Por defecto, los próximos; con scope=past, los ya celebrados (el más reciente primero). Puedes quedarte con los de un curso o producto y con los que empiezan entre dos fechas. Sin esos parámetros responde igual que siempre. Las credenciales de emisión nunca viajan.","security":[{"apiKey":[]}],"parameters":[{"name":"scope","in":"query","required":false,"description":"upcoming (por defecto): los que aún no han terminado, en orden cronológico. past: los terminados o cancelados, del más reciente al más antiguo. all: todos, en orden cronológico.","schema":{"type":"string","enum":["upcoming","past","all"]}},{"name":"product_id","in":"query","required":false,"description":"Solo las sesiones cuya audiencia incluye este producto, sea su producto principal o uno más.","schema":{"type":"integer"}},{"name":"from","in":"query","required":false,"description":"Solo las sesiones que empiezan en esta fecha o después. ISO 8601 con zona (2026-10-01T00:00:00Z) o un día (2026-10-01, medianoche UTC).","schema":{"type":"string"}},{"name":"until","in":"query","required":false,"description":"Solo las sesiones que empiezan antes de esta fecha (no incluida). Mismo formato que from.","schema":{"type":"string"}},{"name":"starting_after","in":"query","required":false,"description":"El next_starting_after de la página anterior. Sigue pidiendo mientras has_more sea true; omítelo en la primera página.","schema":{"type":"integer"}},{"name":"limit","in":"query","required":false,"schema":{"type":"integer"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"examples":{"example":{"value":"{\n  \"object\": \"list\",\n  \"data\": [{\n    \"object\": \"webinar\",\n    \"id\": 410,\n    \"title\": \"Clase en directo: módulo 3\",\n    \"start_at\": \"2026-10-07T16:00:00.000Z\",\n    \"duration_minutes\": 90,\n    \"status\": \"ended\",\n    \"audience_product_id\": 3161,\n    \"audience_product_ids\": [3161],\n    \"replay_published\": true,\n    \"registrant_count\": 48\n  }],\n  \"has_more\": true,\n  \"next_starting_after\": 410\n}"}}}}}}},"post":{"operationId":"webinars_create","tags":["Webinars"],"summary":"webinars_create","security":[{"apiKey":[]}],"responses":{"201":{"description":"OK","content":{"application/json":{"examples":{"example":{"value":"{\n  \"object\": \"list\",\n  \"data\": [{\n    \"object\": \"webinar\",\n    \"id\": 433,\n    \"title\": \"Asset allocation\",\n    \"start_at\": \"2026-10-14T16:00:00.000Z\",\n    \"duration_minutes\": 120,\n    \"timezone\": \"Europe/Madrid\",\n    \"status\": \"scheduled\",\n    \"audience_product_ids\": [3161],\n    \"session_kind\": \"group\",\n    \"broadcast_source\": \"clonify\",\n    \"public_slug\": \"42e59b8bdc0d\",\n    \"replay_published\": false,\n    \"auto_publish_replay\": true,\n    \"registrant_count\": 0\n  }],\n  \"has_more\": false,\n  \"next_starting_after\": null,\n  \"calendar_activated\": false\n}"}}}}}},"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"title":{"type":"string"},"start_at":{"type":"string"},"description":{"type":"string"},"duration_minutes":{"type":"integer"},"timezone":{"type":"string"},"capacity":{"type":"integer"},"audience_product_ids":{"type":"array","items":{"type":"integer"}},"session_kind":{"type":"string","enum":["broadcast","group"]},"broadcast_source":{"type":"string","enum":["clonify","external"]},"recurrence":{"type":"string","enum":["once","weekly"]},"recurrence_end":{"type":"string"},"auto_publish_replay":{"type":"boolean"}},"required":["title","start_at"]}}}}}},"/webinars/{id}":{"get":{"operationId":"webinars_get","tags":["Webinars"],"summary":"webinars_get","security":[{"apiKey":[]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"integer"}}],"responses":{"200":{"description":"OK"}}},"patch":{"operationId":"webinars_update","tags":["Webinars"],"summary":"webinars_update","security":[{"apiKey":[]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"integer"}}],"responses":{"200":{"description":"OK"}},"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"title":{"type":"string"},"description":{"type":"string","nullable":true},"start_at":{"type":"string"},"duration_minutes":{"type":"integer"},"capacity":{"type":"integer","nullable":true},"audience_product_ids":{"type":"array","items":{"type":"integer"}},"session_kind":{"type":"string","enum":["broadcast","group"]},"broadcast_source":{"type":"string","enum":["clonify","external"]},"auto_publish_replay":{"type":"boolean"}},"required":[]}}}}}},"/webinars/{id}/registrants":{"get":{"operationId":"webinar_registrants_list","tags":["Webinars"],"summary":"Listar inscritos de un webinar","description":"Nombre, email y estado (registrado / asistió / no vino) de cada inscrito. Su token de acceso a la sala nunca viaja.","security":[{"apiKey":[]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"integer"}}],"responses":{"200":{"description":"OK"}}}},"/webinars/attendance":{"get":{"operationId":"webinars_attendance","tags":["Webinars"],"summary":"Exportar la asistencia a los directos de un curso","description":"Una fila por sesión y persona de todas las sesiones de un curso o producto que ya han empezado (las canceladas no salen), para no pedir los inscritos sesión a sesión. status vale attended cuando consta que la persona llegó al directo (reprodujo la emisión o llegó a la puerta de la mesa de grupo); registered_not_attended si tiene inscripción pero no consta su entrada; no_show si es inscrita de una sesión pública y se la marcó ausente al terminar. En las 24 horas previas a una sesión de curso, Clonify inscribe a quien tiene acceso a sus productos (hasta 500 por producto, y a nadie si el producto está abierto a cualquiera), así que registered_not_attended suele significar «invitado y no vino»; quien queda fuera de ese tope no tiene inscripción. En una sesión compartida por varios productos solo salen los alumnos del product_id pedido: se quita a quien, al empezar la sesión, solo tenía acceso por los otros productos, y se mantiene a quien no tiene ningún acceso registrado. Con include_not_joined=true se añaden filas not_joined: personas con acceso al producto cuando empezó la sesión y sin inscripción. Ese estado se DEDUCE de los accesos que constan hoy (una compra o etiqueta de acceso desde que se registró en Clonify, una inclusión en un pack desde que se abrió) y de su caducidad; no es un registro de lo que hizo la persona, no recupera accesos retirados o devueltos después y, en productos abiertos a cualquiera, solo incluye a quien tiene un acceso registrado. status_source dice de dónde sale cada fila (registration o access). live_seconds mide la conexión confirmada a la mesa de grupo o el tiempo de reproducción visible de la emisión, y replay_watched_seconds la repetición vista; los dos son null si no hay medición, que empezó en septiembre de 2026 y no se reconstruye hacia atrás. Son datos de conexión y reproducción, no prueba de atención. Una página puede traer menos filas que limit, o ninguna, con has_more todavía a true: sigue pidiendo hasta que sea false. El objeto measurement de cada respuesta resume qué mide cada campo.","security":[{"apiKey":[]}],"parameters":[{"name":"product_id","in":"query","required":true,"description":"El curso o producto. Salen las sesiones cuya audiencia lo incluye, y las filas not_joined se calculan sobre el acceso a este producto. Obligatorio.","schema":{"type":"integer"}},{"name":"from","in":"query","required":false,"description":"Solo las sesiones que empiezan en esta fecha o después. ISO 8601 con zona (2026-10-01T00:00:00Z) o un día (2026-10-01, medianoche UTC).","schema":{"type":"string"}},{"name":"until","in":"query","required":false,"description":"Solo las sesiones que empiezan antes de esta fecha (no incluida). Mismo formato que from.","schema":{"type":"string"}},{"name":"webinar_id","in":"query","required":false,"description":"Limita la exportación a una sola sesión del producto.","schema":{"type":"integer"}},{"name":"include_not_joined","in":"query","required":false,"description":"true añade a quien tenía acceso al producto cuando empezó la sesión y no tiene inscripción (status not_joined, deducido del acceso). Por defecto false: solo inscripciones.","schema":{"type":"boolean"}},{"name":"starting_after","in":"query","required":false,"description":"El next_starting_after de la página anterior, tal cual (es un cursor opaco). Omítelo en la primera página.","schema":{"type":"string"}},{"name":"limit","in":"query","required":false,"description":"Máximo de filas por página: 1–200; por defecto 100.","schema":{"type":"integer"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"examples":{"example":{"value":"{\n  \"object\": \"list\",\n  \"data\": [{\n    \"object\": \"webinar_attendance\",\n    \"id\": \"WzQxMCwiYW5hQGV4YW1wbGUuY29tIl0\",\n    \"webinar_id\": 410,\n    \"webinar_title\": \"Clase en directo: módulo 3\",\n    \"webinar_start_at\": \"2026-10-07T16:00:00.000Z\",\n    \"webinar_status\": \"ended\",\n    \"session_kind\": \"group\",\n    \"registrant_id\": 9120,\n    \"contact_id\": 3,\n    \"visitor_id\": 88,\n    \"name\": \"Ana Pérez\",\n    \"email\": \"ana@example.com\",\n    \"status\": \"attended\",\n    \"status_source\": \"registration\",\n    \"registered_at\": \"2026-10-06T16:00:04.000Z\",\n    \"live_seconds\": 4980,\n    \"live_seconds_source\": \"group_room_connections\",\n    \"replay_watched\": false,\n    \"replay_watched_seconds\": null\n  }, {\n    \"object\": \"webinar_attendance\",\n    \"id\": \"WzQxMCwibHVpc0BleGFtcGxlLmNvbSJd\",\n    \"webinar_id\": 410,\n    \"webinar_title\": \"Clase en directo: módulo 3\",\n    \"webinar_start_at\": \"2026-10-07T16:00:00.000Z\",\n    \"webinar_status\": \"ended\",\n    \"session_kind\": \"group\",\n    \"registrant_id\": null,\n    \"contact_id\": 41,\n    \"visitor_id\": 97,\n    \"name\": \"Luis Gómez\",\n    \"email\": \"luis@example.com\",\n    \"status\": \"not_joined\",\n    \"status_source\": \"access\",\n    \"registered_at\": null,\n    \"live_seconds\": null,\n    \"live_seconds_source\": null,\n    \"replay_watched\": true,\n    \"replay_watched_seconds\": 1830\n  }],\n  \"has_more\": true,\n  \"next_starting_after\": \"WzQxMCwibHVpc0BleGFtcGxlLmNvbSJd\",\n  \"measurement\": { \"status\": \"…\", \"not_joined\": \"…\", \"live_seconds\": \"…\" }\n}"}}}}}}}},"/usage":{"get":{"operationId":"usage_get","tags":["Cuenta y consumo"],"summary":"Leer consumo del plan","description":"Respuestas del clon y palabras del conocimiento del periodo actual. Útil para que tu sistema se autorregule (p. ej. pausar una ingesta si quedan pocas palabras).","security":[{"apiKey":[]}],"responses":{"200":{"description":"OK","content":{"application/json":{"examples":{"example":{"value":"{\n  \"object\": \"usage\",\n  \"plan\": { \"tier\": \"pro\", \"name\": \"Pro\" },\n  \"period_start\": \"2026-07-01\",\n  \"unlimited\": false,\n  \"responses\": { \"granted\": 2200, \"used\": 340, \"remaining\": 1860 },\n  \"words\": { \"granted\": 5000000, \"current\": 812000, \"remaining\": 4188000 },\n  \"overage\": { \"responses\": 0, \"charged_cents\": 0 }\n}"}}}}}}}}},"components":{"securitySchemes":{"apiKey":{"type":"http","scheme":"bearer","description":"Para conectar un chat elige Solo conversar. Crea tu llave en el panel: Configuración → Claves de API y webhooks. Puedes tener varias (una por sistema conectado), cada una con permisos de solo lectura, de lectura y escritura, o totales (con borrado). La llave se muestra UNA sola vez: guárdala como una contraseña. Envíala en cada petición en la cabecera Authorization."}}}}