{"openapi":"3.1.0","info":{"title":"FlujosChat — API pública","version":"1.0.0","summary":"API REST para integrar WhatsApp Business y el CRM de FlujosChat.","description":"Superficie estable para integraciones de terceros: enviar mensajes y plantillas de\nWhatsApp, leer y actualizar conversaciones, sincronizar la ficha del cliente y\nrecibir eventos por webhook.\n\n- Autenticación por **API key** de empresa (`Authorization: Bearer fjc_live_...`).\n- Todo queda acotado a la empresa de la key: no hace falta mandar el id de empresa.\n- Paginación por **cursor**; los `POST` de envío aceptan `Idempotency-Key`.\n- Los errores traen siempre un `error.code` estable.\n\nDocumentación con ejemplos: https://www.flujoschat.com/api-publica","contact":{"name":"FlujosChat","url":"https://www.flujoschat.com/contacto"}},"servers":[{"url":"https://api.flujoschat.com/api/public/v1","description":"Producción"}],"tags":[{"name":"Cuenta","description":"Identidad de la key y consumo del plan."},{"name":"Conversaciones","description":"Leer y actualizar conversaciones y su historial."},{"name":"Mensajes","description":"Enviar WhatsApp (texto o plantilla aprobada)."},{"name":"Campos personalizados","description":"La ficha CRM del cliente."},{"name":"Plantillas","description":"Plantillas aprobadas por Meta."}],"security":[{"ApiKeyBearer":[]}],"paths":{"/me":{"get":{"tags":["Cuenta"],"summary":"Identidad de la key","description":"Empresa, plan y scopes. Es la llamada para comprobar que una key funciona.","operationId":"getMe","responses":{"200":{"description":"Operación exitosa","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","example":true},"message":{"type":"string"},"data":{"type":"object","properties":{"company":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"}}},"plan":{"type":"string","example":"PRO"},"apiKey":{"type":"object","properties":{"name":{"type":"string"},"scopes":{"type":"array","items":{"type":"string","enum":["conversations:read","conversations:write","messages:send","templates:read","templates:send","custom_fields:read","custom_fields:write","usage:read"]}}}}}}}}}}},"401":{"description":"Key ausente o inválida","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"MISSING_API_KEY":{"value":{"success":false,"message":"Descripción del problema","error":{"code":"MISSING_API_KEY"}}},"INVALID_API_KEY":{"value":{"success":false,"message":"Descripción del problema","error":{"code":"INVALID_API_KEY"}}}}}}},"402":{"description":"Suscripción inactiva","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"SUBSCRIPTION_LOCKED":{"value":{"success":false,"message":"Descripción del problema","error":{"code":"SUBSCRIPTION_LOCKED"}}}}}}},"403":{"description":"El plan no incluye API","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"API_ACCESS_NOT_INCLUDED":{"value":{"success":false,"message":"Descripción del problema","error":{"code":"API_ACCESS_NOT_INCLUDED"}}}}}}}}}},"/usage":{"get":{"tags":["Cuenta"],"summary":"Consumo del mes","operationId":"getUsage","security":[{"ApiKeyBearer":["usage:read"]}],"responses":{"200":{"description":"Operación exitosa","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","example":true},"message":{"type":"string"},"data":{"type":"object","properties":{"period":{"type":"string","example":"2026-07"},"used":{"type":"integer"},"limit":{"type":"integer","nullable":true,"description":"`null` = ilimitado"},"remaining":{"type":"integer","nullable":true}}}}}}}},"403":{"description":"Falta el scope","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"INSUFFICIENT_SCOPE":{"value":{"success":false,"message":"Descripción del problema","error":{"code":"INSUFFICIENT_SCOPE"}}}}}}}}}},"/conversations":{"get":{"tags":["Conversaciones"],"summary":"Listar conversaciones","description":"Para sincronización incremental usá `updatedAfter` + `order=updatedAt`: con el orden por defecto (`createdAt`) solo se detectan conversaciones nuevas, no las que cambiaron.","operationId":"listConversations","security":[{"ApiKeyBearer":["conversations:read"]}],"parameters":[{"name":"status","in":"query","schema":{"type":"string","enum":["IN_PROGRESS","CLOSED"]}},{"name":"phone","in":"query","description":"Formato internacional, p. ej. `+51987654321`.","schema":{"type":"string"}},{"name":"channel","in":"query","description":"Canal por el que escribe el cliente.","schema":{"type":"string","enum":["WHATSAPP","INSTAGRAM","TELEGRAM","TIKTOK","MESSENGER"]}},{"name":"updatedAfter","in":"query","description":"Solo conversaciones con `updatedAt` posterior (ISO 8601).","schema":{"type":"string","format":"date-time"}},{"name":"order","in":"query","schema":{"type":"string","enum":["createdAt","updatedAt","lastMessageAt"],"default":"createdAt"}},{"name":"include","in":"query","description":"Lista separada por comas. `customFields` requiere el scope `custom_fields:read`.","schema":{"type":"string","example":"customFields"}},{"name":"limit","in":"query","description":"Elementos por página (1–100).","schema":{"type":"integer","minimum":1,"maximum":100,"default":20}},{"name":"cursor","in":"query","description":"Id del último elemento de la página anterior (`nextCursor`). Un cursor inexistente devuelve 400 `INVALID_CURSOR`.","schema":{"type":"string"}}],"responses":{"200":{"description":"Operación exitosa","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","example":true},"message":{"type":"string"},"data":{"type":"object","properties":{"conversations":{"type":"array","items":{"$ref":"#/components/schemas/Conversation"}},"nextCursor":{"type":"string","nullable":true,"description":"Pasalo como `?cursor=` para la página siguiente. `null` = no hay más."}}}}}}}},"400":{"description":"Parámetro inválido","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"INVALID_STATUS":{"value":{"success":false,"message":"Descripción del problema","error":{"code":"INVALID_STATUS"}}},"INVALID_PHONE":{"value":{"success":false,"message":"Descripción del problema","error":{"code":"INVALID_PHONE"}}},"INVALID_PARAMETER":{"value":{"success":false,"message":"Descripción del problema","error":{"code":"INVALID_PARAMETER"}}},"INVALID_CURSOR":{"value":{"success":false,"message":"Descripción del problema","error":{"code":"INVALID_CURSOR"}}}}}}},"403":{"description":"Falta el scope","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"INSUFFICIENT_SCOPE":{"value":{"success":false,"message":"Descripción del problema","error":{"code":"INSUFFICIENT_SCOPE"}}}}}}}}}},"/conversations/{id}":{"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"get":{"tags":["Conversaciones"],"summary":"Detalle de una conversación","operationId":"getConversation","security":[{"ApiKeyBearer":["conversations:read"]}],"parameters":[{"name":"include","in":"query","schema":{"type":"string"}}],"responses":{"200":{"description":"Operación exitosa","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","example":true},"message":{"type":"string"},"data":{"type":"object","properties":{"conversation":{"$ref":"#/components/schemas/Conversation"}}}}}}}},"404":{"description":"No existe o es de otra empresa","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"CONVERSATION_NOT_FOUND":{"value":{"success":false,"message":"Descripción del problema","error":{"code":"CONVERSATION_NOT_FOUND"}}}}}}}}},"patch":{"tags":["Conversaciones"],"summary":"Actualizar estado operativo","description":"Cerrar/reabrir, pausar el bot para que atienda un humano, corregir el nombre.","operationId":"updateConversation","security":[{"ApiKeyBearer":["conversations:write"]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","minProperties":1,"properties":{"status":{"type":"string","enum":["IN_PROGRESS","CLOSED"]},"botPaused":{"type":"boolean"},"customerName":{"type":"string","nullable":true,"maxLength":120}}},"example":{"status":"CLOSED","botPaused":true}}}},"responses":{"200":{"description":"Operación exitosa","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","example":true},"message":{"type":"string"},"data":{"type":"object","properties":{"conversation":{"$ref":"#/components/schemas/Conversation"}}}}}}}},"400":{"description":"Body inválido","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"EMPTY_UPDATE":{"value":{"success":false,"message":"Descripción del problema","error":{"code":"EMPTY_UPDATE"}}},"INVALID_STATUS":{"value":{"success":false,"message":"Descripción del problema","error":{"code":"INVALID_STATUS"}}},"INVALID_PARAMETER":{"value":{"success":false,"message":"Descripción del problema","error":{"code":"INVALID_PARAMETER"}}}}}}},"404":{"description":"No existe","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"CONVERSATION_NOT_FOUND":{"value":{"success":false,"message":"Descripción del problema","error":{"code":"CONVERSATION_NOT_FOUND"}}}}}}}}}},"/conversations/{id}/messages":{"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"get":{"tags":["Conversaciones"],"summary":"Historial de mensajes","description":"Del más reciente al más antiguo.","operationId":"listConversationMessages","security":[{"ApiKeyBearer":["conversations:read"]}],"parameters":[{"name":"limit","in":"query","description":"Elementos por página (1–100).","schema":{"type":"integer","minimum":1,"maximum":100,"default":20}},{"name":"cursor","in":"query","description":"Id del último elemento de la página anterior (`nextCursor`). Un cursor inexistente devuelve 400 `INVALID_CURSOR`.","schema":{"type":"string"}}],"responses":{"200":{"description":"Operación exitosa","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","example":true},"message":{"type":"string"},"data":{"type":"object","properties":{"messages":{"type":"array","items":{"$ref":"#/components/schemas/Message"}},"nextCursor":{"type":"string","nullable":true,"description":"Pasalo como `?cursor=` para la página siguiente. `null` = no hay más."}}}}}}}},"404":{"description":"No existe","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"CONVERSATION_NOT_FOUND":{"value":{"success":false,"message":"Descripción del problema","error":{"code":"CONVERSATION_NOT_FOUND"}}}}}}}}}},"/custom-fields":{"get":{"tags":["Campos personalizados"],"summary":"Catálogo de campos","description":"Las definiciones de la empresa. De acá salen los `fieldId` para escribir.","operationId":"listCustomFields","security":[{"ApiKeyBearer":["custom_fields:read"]}],"parameters":[{"name":"fieldCategory","in":"query","schema":{"type":"string","enum":["USER","BOT"]}}],"responses":{"200":{"description":"Operación exitosa","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","example":true},"message":{"type":"string"},"data":{"type":"object","properties":{"customFields":{"type":"array","items":{"$ref":"#/components/schemas/CustomField"}}}}}}}}},"403":{"description":"Falta el scope","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"INSUFFICIENT_SCOPE":{"value":{"success":false,"message":"Descripción del problema","error":{"code":"INSUFFICIENT_SCOPE"}}}}}}}}}},"/conversations/{id}/custom-fields":{"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"get":{"tags":["Campos personalizados"],"summary":"Ficha CRM del cliente","operationId":"listConversationCustomFields","security":[{"ApiKeyBearer":["custom_fields:read"]}],"responses":{"200":{"description":"Operación exitosa","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","example":true},"message":{"type":"string"},"data":{"type":"object","properties":{"customFields":{"type":"array","items":{"$ref":"#/components/schemas/CustomFieldValue"}}}}}}}}},"404":{"description":"No existe","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"CONVERSATION_NOT_FOUND":{"value":{"success":false,"message":"Descripción del problema","error":{"code":"CONVERSATION_NOT_FOUND"}}}}}}}}}},"/conversations/{id}/custom-fields/{fieldId}":{"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}},{"name":"fieldId","in":"path","required":true,"schema":{"type":"string"}}],"put":{"tags":["Campos personalizados"],"summary":"Escribir un campo","description":"Upsert idempotente. Toca `conversation.updatedAt`, así que la sincronización por `updatedAfter` ve el cambio.","operationId":"setConversationCustomField","security":[{"ApiKeyBearer":["custom_fields:write"]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["value"],"properties":{"value":{"type":"string","example":"45678912"}}}}}},"responses":{"200":{"description":"Operación exitosa","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","example":true},"message":{"type":"string"},"data":{"type":"object","properties":{"customField":{"$ref":"#/components/schemas/CustomFieldValue"}}}}}}}},"400":{"description":"Body inválido","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"MISSING_FIELD":{"value":{"success":false,"message":"Descripción del problema","error":{"code":"MISSING_FIELD"}}},"INVALID_PARAMETER":{"value":{"success":false,"message":"Descripción del problema","error":{"code":"INVALID_PARAMETER"}}}}}}},"404":{"description":"No existe","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"CONVERSATION_NOT_FOUND":{"value":{"success":false,"message":"Descripción del problema","error":{"code":"CONVERSATION_NOT_FOUND"}}},"CUSTOM_FIELD_NOT_FOUND":{"value":{"success":false,"message":"Descripción del problema","error":{"code":"CUSTOM_FIELD_NOT_FOUND"}}}}}}}}}},"/templates":{"get":{"tags":["Plantillas"],"summary":"Listar plantillas","description":"Por defecto solo las `APPROVED` (las únicas que Meta permite enviar). Incluye `variableMapping`, que dice qué espera cada `{{n}}`.","operationId":"listTemplates","security":[{"ApiKeyBearer":["templates:read"]}],"parameters":[{"name":"status","in":"query","schema":{"type":"string","enum":["APPROVED","PENDING","REJECTED","PAUSED","DISABLED","all"],"default":"APPROVED"}},{"name":"category","in":"query","schema":{"type":"string","enum":["MARKETING","UTILITY","AUTHENTICATION"]}},{"name":"limit","in":"query","description":"Elementos por página (1–100).","schema":{"type":"integer","minimum":1,"maximum":100,"default":20}},{"name":"cursor","in":"query","description":"Id del último elemento de la página anterior (`nextCursor`). Un cursor inexistente devuelve 400 `INVALID_CURSOR`.","schema":{"type":"string"}}],"responses":{"200":{"description":"Operación exitosa","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","example":true},"message":{"type":"string"},"data":{"type":"object","properties":{"templates":{"type":"array","items":{"$ref":"#/components/schemas/Template"}},"nextCursor":{"type":"string","nullable":true,"description":"Pasalo como `?cursor=` para la página siguiente. `null` = no hay más."}}}}}}}},"400":{"description":"Parámetro inválido","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"INVALID_PARAMETER":{"value":{"success":false,"message":"Descripción del problema","error":{"code":"INVALID_PARAMETER"}}},"INVALID_CURSOR":{"value":{"success":false,"message":"Descripción del problema","error":{"code":"INVALID_CURSOR"}}}}}}}}}},"/templates/{id}":{"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"get":{"tags":["Plantillas"],"summary":"Detalle de una plantilla","operationId":"getTemplate","security":[{"ApiKeyBearer":["templates:read"]}],"responses":{"200":{"description":"Operación exitosa","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","example":true},"message":{"type":"string"},"data":{"type":"object","properties":{"template":{"$ref":"#/components/schemas/Template"}}}}}}}},"404":{"description":"No existe","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"TEMPLATE_NOT_FOUND":{"value":{"success":false,"message":"Descripción del problema","error":{"code":"TEMPLATE_NOT_FOUND"}}}}}}}}}},"/messages":{"post":{"tags":["Mensajes"],"summary":"Enviar un mensaje","description":"En WhatsApp e Instagram, `type: \"text\"` solo funciona dentro de la ventana de 24 h de\nMeta (el cliente te escribió hace menos de 24 h). Fuera de esa ventana, en WhatsApp la\núnica forma de escribir es `type: \"template\"` con una plantilla aprobada; en Instagram\nno hay plantillas, así que hay que esperar a que el cliente vuelva a escribir\n(`INSTAGRAM_WINDOW_EXPIRED`). En **TikTok** la ventana es de 48 h desde el último mensaje\ndel cliente y admite hasta 10 mensajes; tampoco hay plantillas (`TIKTOK_WINDOW_EXPIRED`).\nEn **Messenger** la ventana es de 24 h, como en Instagram (`MESSENGER_WINDOW_EXPIRED`).\n**Telegram no tiene ventana**: se le puede escribir\nmientras no haya bloqueado al bot (`TELEGRAM_BLOCKED_BY_USER`).\n\nPara responder por **Instagram**, **Telegram**, **TikTok** o **Messenger** se direcciona con `conversationId` en vez\nde `to`: esos clientes no tienen teléfono ni ninguna dirección que puedas escribir.\n\nMandá `Idempotency-Key` siempre: si tu sistema reintenta, no se envía dos veces.","operationId":"sendMessage","security":[{"ApiKeyBearer":["messages:send","templates:send"]}],"parameters":[{"name":"Idempotency-Key","in":"header","description":"Valor único por acción de negocio (p. ej. el id del pedido). TTL 24 h. Reusarla con otro body devuelve 409.","schema":{"type":"string","maxLength":255}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"oneOf":[{"$ref":"#/components/schemas/SendTextRequest"},{"$ref":"#/components/schemas/SendTemplateRequest"}]},"examples":{"texto":{"summary":"Texto libre (dentro de las 24 h)","value":{"to":"+51987654321","type":"text","text":"Tu pedido #4512 va en camino 🚚"}},"porConversacion":{"summary":"Texto a un hilo existente (única forma en Instagram)","value":{"conversationId":"c0ffee00-0000-0000-0000-000000000000","type":"text","text":"Te mando la talla M, ¿te va? 👟"}},"plantilla":{"summary":"Plantilla aprobada (puede iniciar la conversación)","value":{"to":"+51987654321","type":"template","templateId":"b7f1c3e0-0000-0000-0000-000000000000","variables":{"1":"4512"},"customerName":"Ana Pérez"}}}}}},"responses":{"201":{"description":"Mensaje aceptado y enviado al canal de la conversación","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","example":true},"message":{"type":"string"},"data":{"type":"object","properties":{"message":{"$ref":"#/components/schemas/Message"},"conversation":{"type":"object","properties":{"id":{"type":"string"},"channel":{"type":"string","enum":["WHATSAPP","INSTAGRAM","TELEGRAM","TIKTOK","MESSENGER"]},"customerPhone":{"type":"string","nullable":true},"customerUserId":{"type":"string","nullable":true},"identifiedBy":{"type":"string","enum":["PHONE","BSUID","INSTAGRAM","TELEGRAM","TIKTOK","MESSENGER"]}}}}}}}}}},"400":{"description":"Body inválido","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"INVALID_PHONE":{"value":{"success":false,"message":"Descripción del problema","error":{"code":"INVALID_PHONE"}}},"INVALID_MESSAGE_TYPE":{"value":{"success":false,"message":"Descripción del problema","error":{"code":"INVALID_MESSAGE_TYPE"}}},"MISSING_FIELD":{"value":{"success":false,"message":"Descripción del problema","error":{"code":"MISSING_FIELD"}}},"TEXT_TOO_LONG":{"value":{"success":false,"message":"Descripción del problema","error":{"code":"TEXT_TOO_LONG"}}},"INVALID_PARAMETER":{"value":{"success":false,"message":"Descripción del problema","error":{"code":"INVALID_PARAMETER"}}}}}}},"403":{"description":"Falta el scope del tipo enviado","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"INSUFFICIENT_SCOPE":{"value":{"success":false,"message":"Descripción del problema","error":{"code":"INSUFFICIENT_SCOPE"}}}}}}},"404":{"description":"Plantilla inexistente, o `conversationId` que no es de tu empresa","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"TEMPLATE_NOT_FOUND":{"value":{"success":false,"message":"Descripción del problema","error":{"code":"TEMPLATE_NOT_FOUND"}}},"CONVERSATION_NOT_FOUND":{"value":{"success":false,"message":"Descripción del problema","error":{"code":"CONVERSATION_NOT_FOUND"}}}}}}},"409":{"description":"Conflicto de idempotencia","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"IDEMPOTENCY_KEY_REUSED":{"value":{"success":false,"message":"Descripción del problema","error":{"code":"IDEMPOTENCY_KEY_REUSED"}}},"IDEMPOTENCY_IN_PROGRESS":{"value":{"success":false,"message":"Descripción del problema","error":{"code":"IDEMPOTENCY_IN_PROGRESS"}}},"CHANNEL_NOT_CONNECTED":{"value":{"success":false,"message":"Descripción del problema","error":{"code":"CHANNEL_NOT_CONNECTED"}}}}}}},"422":{"description":"Regla del canal","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"WHATSAPP_24H_WINDOW_EXPIRED":{"value":{"success":false,"message":"Descripción del problema","error":{"code":"WHATSAPP_24H_WINDOW_EXPIRED"}}},"INSTAGRAM_WINDOW_EXPIRED":{"value":{"success":false,"message":"Descripción del problema","error":{"code":"INSTAGRAM_WINDOW_EXPIRED"}}},"TIKTOK_WINDOW_EXPIRED":{"value":{"success":false,"message":"Descripción del problema","error":{"code":"TIKTOK_WINDOW_EXPIRED"}}},"MESSENGER_WINDOW_EXPIRED":{"value":{"success":false,"message":"Descripción del problema","error":{"code":"MESSENGER_WINDOW_EXPIRED"}}},"CHANNEL_NOT_SUPPORTED":{"value":{"success":false,"message":"Descripción del problema","error":{"code":"CHANNEL_NOT_SUPPORTED"}}},"TEMPLATE_NOT_APPROVED":{"value":{"success":false,"message":"Descripción del problema","error":{"code":"TEMPLATE_NOT_APPROVED"}}}}}}},"429":{"description":"Límite alcanzado","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"RATE_LIMITED":{"value":{"success":false,"message":"Descripción del problema","error":{"code":"RATE_LIMITED"}}},"MONTHLY_QUOTA_EXCEEDED":{"value":{"success":false,"message":"Descripción del problema","error":{"code":"MONTHLY_QUOTA_EXCEEDED"}}}}}}},"502":{"description":"Meta rechazó el envío","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"PROVIDER_ERROR":{"value":{"success":false,"message":"Descripción del problema","error":{"code":"PROVIDER_ERROR"}}}}}}}}}}},"components":{"securitySchemes":{"ApiKeyBearer":{"type":"http","scheme":"bearer","description":"API key de empresa: `Authorization: Bearer fjc_live_...`. Se crea en el dashboard (Configuración → API Keys) y se muestra una sola vez. También se acepta el header `X-API-Key`."}},"schemas":{"Error":{"type":"object","properties":{"success":{"type":"boolean","example":false},"message":{"type":"string","description":"Mensaje humano, en español. Puede cambiar."},"error":{"type":"object","properties":{"code":{"type":"string","enum":["MISSING_API_KEY","INVALID_API_KEY","COMPANY_SUSPENDED","SUBSCRIPTION_LOCKED","API_ACCESS_NOT_INCLUDED","INSUFFICIENT_SCOPE","RATE_LIMITED","MONTHLY_QUOTA_EXCEEDED","INVALID_PHONE","INVALID_STATUS","INVALID_CURSOR","INVALID_PARAMETER","INVALID_MESSAGE_TYPE","MISSING_FIELD","TEXT_TOO_LONG","EMPTY_UPDATE","CONVERSATION_NOT_FOUND","TEMPLATE_NOT_FOUND","CUSTOM_FIELD_NOT_FOUND","TEMPLATE_NOT_APPROVED","WHATSAPP_24H_WINDOW_EXPIRED","INSTAGRAM_WINDOW_EXPIRED","TIKTOK_WINDOW_EXPIRED","MESSENGER_WINDOW_EXPIRED","CHANNEL_NOT_SUPPORTED","CHANNEL_NOT_CONNECTED","PROVIDER_ERROR","IDEMPOTENCY_KEY_REUSED","IDEMPOTENCY_IN_PROGRESS","INTERNAL_ERROR"],"description":"Código estable: ramificá por este, no por el mensaje."}}}}},"Conversation":{"type":"object","properties":{"id":{"type":"string"},"channel":{"type":"string","enum":["WHATSAPP","INSTAGRAM","TELEGRAM","TIKTOK","MESSENGER"],"description":"Canal por el que escribe el cliente."},"customerPhone":{"type":"string","nullable":true,"example":"+51987654321","description":"E.164 con `+`. Es `null` cuando el cliente no tiene teléfono: porque adoptó *username* en WhatsApp y Meta no lo envía, o porque el canal no usa teléfonos. En esos casos se identifica por `customerUserId`."},"customerUserId":{"type":"string","nullable":true,"description":"Identificador del cliente cuando no hay teléfono: el BSUID (business-scoped user id) de Meta si `identifiedBy` es `BSUID` o, en otro canal, el id del cliente en ese canal (el IGSID si es `INSTAGRAM`; en `TELEGRAM`, `<botId>:<userId>`, porque el mismo usuario escribiendo a dos bots de tu empresa son dos conversaciones; en `TIKTOK`, el `conversation_id`). Trátalo como opaco. Es estable para tu cuenta, pero no es global: no se puede cruzar con otra empresa ni con un teléfono."},"customerUsername":{"type":"string","nullable":true,"description":"Usuario público (`@usuario`), sin la arroba. Solo en Instagram, Telegram y TikTok (en Telegram, si el cliente tiene uno); en WhatsApp es siempre `null`. Puede cambiar: no lo uses como clave."},"identifiedBy":{"type":"string","enum":["PHONE","BSUID","INSTAGRAM","TELEGRAM","TIKTOK","MESSENGER","UNKNOWN"],"description":"Por qué identificador se reconoce al cliente. `UNKNOWN` no debería aparecer nunca: significa que el canal de esa conversación es más nuevo que esta versión de la API — trátalo como \"no puedo identificarlo\", nunca como un teléfono."},"customerName":{"type":"string","nullable":true},"status":{"type":"string","enum":["IN_PROGRESS","CLOSED"]},"engagementLevel":{"type":"string","nullable":true,"enum":["CONFIRMAR_COMPRA","INTERESADO_INTERACTUADOR","INTERESADO_MENOR",null]},"hasPurchaseConfirmation":{"type":"boolean"},"botPaused":{"type":"boolean"},"aiControlled":{"type":"boolean"},"aiTransferReason":{"type":"string","nullable":true,"description":"Por qué la IA dejó la conversación esperando a un humano."},"lastMessageAt":{"type":"string","format":"date-time","nullable":true},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"},"customFields":{"type":"array","description":"Solo con `?include=customFields`.","items":{"$ref":"#/components/schemas/CustomFieldValue"}}}},"Message":{"type":"object","properties":{"id":{"type":"string"},"conversationId":{"type":"string"},"channel":{"type":"string","enum":["WHATSAPP","INSTAGRAM","TELEGRAM","TIKTOK","MESSENGER"],"description":"Canal por el que viajó el mensaje."},"direction":{"type":"string","enum":["INBOUND","OUTBOUND"]},"type":{"type":"string","enum":["TEXT","IMAGE","AUDIO","DOCUMENT","VIDEO","INTERACTIVE","TEMPLATE","LOCATION","STICKER","REACTION","CONTACTS"]},"text":{"type":"string","nullable":true,"description":"Texto legible del mensaje."},"content":{"type":"object","additionalProperties":true},"status":{"type":"string","enum":["PENDING","SENT","DELIVERED","READ","FAILED"]},"sentAt":{"type":"string","format":"date-time","nullable":true},"deliveredAt":{"type":"string","format":"date-time","nullable":true},"readAt":{"type":"string","format":"date-time","nullable":true},"createdAt":{"type":"string","format":"date-time"}}},"CustomField":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string","example":"DNI"},"fieldType":{"type":"string","enum":["TEXT","EMAIL","PHONE","NUMBER","SELECT","TEXTAREA","DATE"]},"fieldCategory":{"type":"string","enum":["USER","BOT"]},"placeholder":{"type":"string","nullable":true},"description":{"type":"string","nullable":true},"isRequired":{"type":"boolean"},"isActive":{"type":"boolean"}}},"CustomFieldValue":{"type":"object","properties":{"fieldId":{"type":"string"},"name":{"type":"string"},"fieldType":{"type":"string"},"fieldCategory":{"type":"string","enum":["USER","BOT"]},"value":{"type":"string"},"updatedAt":{"type":"string","format":"date-time"}}},"Template":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string","description":"Nombre en Meta (snake_case)."},"displayName":{"type":"string"},"category":{"type":"string","enum":["MARKETING","UTILITY","AUTHENTICATION"]},"language":{"type":"string","example":"es"},"status":{"type":"string","enum":["PENDING","APPROVED","REJECTED","PAUSED","DISABLED"]},"headerType":{"type":"string","nullable":true},"headerContent":{"type":"string","nullable":true},"body":{"type":"string","example":"Tu pedido #{{1}} sale {{2}}"},"footer":{"type":"string","nullable":true},"buttons":{"type":"array","nullable":true,"items":{"type":"object","additionalProperties":true}},"variableMapping":{"type":"object","nullable":true,"additionalProperties":true,"description":"Qué espera cada `{{n}}`. Lo que no esté mapeado se manda en `variables`."},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"}}},"SendTextRequest":{"type":"object","title":"Texto","description":"Decí a quién le escribís de UNA de las dos formas: `to` (un número, y siempre crea o reusa una conversación de WhatsApp) **o** `conversationId` (el hilo, sirva el canal que sirva). Mandar las dos devuelve 400: son dos maneras de decir lo mismo y elegir una en silencio es cómo se le manda un mensaje al cliente equivocado.","required":["text"],"oneOf":[{"required":["to"]},{"required":["conversationId"]}],"properties":{"to":{"type":"string","example":"+51987654321"},"conversationId":{"type":"string","description":"De `GET /conversations`. Es la única forma de responder por Instagram, Telegram o TikTok, donde el cliente no tiene ninguna dirección que puedas escribir."},"type":{"type":"string","enum":["text"],"default":"text"},"text":{"type":"string","description":"WhatsApp: hasta 4096 caracteres (pasarse devuelve `TEXT_TOO_LONG`). Instagram y Telegram no tienen ese tope: el texto largo sale troceado en varios mensajes (en Instagram, cada 1000 **bytes** UTF-8)."},"customerName":{"type":"string","nullable":true,"description":"Solo se aplica con `to` (al crear o reusar la conversación). Sobre un `conversationId` se ignora: para renombrar un contacto está `PATCH /conversations/{id}`, que pide su propio scope."}}},"SendTemplateRequest":{"type":"object","title":"Plantilla","description":"Las plantillas son de WhatsApp: se direccionan por `to` y sobre una conversación de Instagram, Telegram o TikTok devuelven 422 `CHANNEL_NOT_SUPPORTED`.","required":["to","type","templateId"],"properties":{"to":{"type":"string","example":"+51987654321"},"type":{"type":"string","enum":["template"]},"templateId":{"type":"string","description":"De `GET /templates`."},"variables":{"type":"object","additionalProperties":{"type":"string"},"example":{"1":"4512"}},"customerName":{"type":"string","nullable":true}}},"WebhookEvent":{"type":"object","description":"Cuerpo que FlujosChat envía a tu endpoint (no es una respuesta de esta API). Firmado en el header `X-FlujosChat-Signature`; rechazá timestamps con más de 300 segundos de desfase y deduplicá por `id`.","properties":{"id":{"type":"string","example":"evt_9f8a7b…","description":"Clave de deduplicación."},"type":{"type":"string","enum":["message.received","message.sent","message.status_updated","conversation.created","conversation.updated","conversation.custom_field_updated"]},"apiVersion":{"type":"string","example":"2026-07-26"},"createdAt":{"type":"string","format":"date-time"},"companyId":{"type":"string"},"data":{"type":"object","additionalProperties":true}}}}},"x-webhook-events":[{"type":"message.received","description":"Un cliente te escribió (por WhatsApp, Instagram, Telegram o TikTok)"},{"type":"message.sent","description":"Se envió un mensaje al cliente (por un agente, un flujo, la IA o la API)"},{"type":"message.status_updated","description":"Meta reportó un cambio de estado de un mensaje enviado (entregado, leído, fallido)"},{"type":"conversation.created","description":"Se abrió una conversación con un cliente nuevo"},{"type":"conversation.updated","description":"Cambió el estado de una conversación (cerrada/reabierta, bot pausado, nombre)"},{"type":"conversation.custom_field_updated","description":"Se escribió un campo personalizado de un cliente"}]}