{"openapi":"3.1.0","info":{"title":"heysteff API","version":"1.0.0","description":"API pública de heysteff: mensajes por WhatsApp (API oficial y QR), Instagram, Messenger y Gmail, contactos, campañas, eventos, conversiones, cobros, reseñas, flujos, el registro de entregas de tus webhooks, números de WhatsApp de heysteff, WhatsApp Flows, llamadas de WhatsApp y clientes con links de conexión para que tus clientes conecten sus propios canales, y la bandeja embebible para meter las conversaciones en tu producto. El agente de IA, con sus custom tools y los hallazgos de tus conversaciones. Autenticación con `Authorization: Bearer <API key>`. Documentación: https://developers.heysteff.com","contact":{"name":"heysteff","url":"https://developers.heysteff.com","email":"info@heysteff.com"}},"servers":[{"url":"https://app.heysteff.com/api"}],"security":[{"bearerAuth":[]}],"externalDocs":{"description":"Documentación para developers","url":"https://developers.heysteff.com/docs/"},"components":{"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","description":"API key del workspace en el header `Authorization: Bearer sk_live_…`. Se crea en Configuración → Desarrolladores y cada key lleva sus scopes."}},"schemas":{"ErrorResponse":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string","example":"validation_error"},"message":{"type":"string","example":"Request validation failed"},"details":{},"fix":{"type":"string"}},"required":["code","message"]}},"required":["error"],"description":"Formato uniforme de error de toda la API: `{ error: { code, message, details? } }`."},"MessageCreated":{"type":"object","properties":{"data":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"conversation_id":{"type":"string","format":"uuid"},"status":{"type":"string","example":"pending"},"content":{"type":["string","null"]},"content_type":{"type":"string","example":"text"},"created_at":{"type":"string","format":"date-time"},"delivery":{"type":"string","enum":["native","quick_replies","text"],"description":"Solo en `interactive`, `location`, `flow`, `voice_call` y `call_permission_request`: cómo sale en el canal. `native` (WhatsApp Cloud; siempre en los tres últimos), `quick_replies` (Instagram y Messenger) o `text` (opciones numeradas o enlace de mapa)."},"degraded":{"type":"boolean","description":"Solo en `interactive` y `location`: `true` si el canal no tiene el formato y sale como `delivery` dice."},"parts":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"content":{"type":"string"}},"required":["id","content"]},"description":"Solo si el texto pasaba el largo del canal (Instagram 1.000, Messenger 2.000, WhatsApp 4.096): las piezas en orden. `id` es la primera."}},"required":["id","conversation_id","status","content","content_type","created_at"]}},"required":["data"]},"ReactionAccepted":{"type":"object","properties":{"data":{"type":"object","properties":{"message_id":{"type":"string","format":"uuid"},"conversation_id":{"type":"string","format":"uuid"},"content_type":{"type":"string","enum":["reaction"]},"emoji":{"type":"string","example":"👍"},"status":{"type":"string","enum":["pending"]}},"required":["message_id","conversation_id","content_type","emoji","status"]}},"required":["data"]},"Message":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"conversation_id":{"type":"string","format":"uuid"},"channel":{"type":"string","example":"whatsapp_cloud"},"direction":{"type":"string","enum":["inbound","outbound"]},"status":{"type":"string","enum":["queued","sent","delivered","read","failed","received","pending_approval","discarded"],"description":"Salientes: `queued` → `sent` → `delivered` → `read`, o `failed`. Entrantes: `received`."},"content":{"type":["string","null"]},"content_type":{"type":"string"},"external_message_id":{"type":["string","null"],"description":"Id del mensaje en el proveedor (wamid, mid…)."},"created_at":{"type":"string","format":"date-time"},"sent_at":{"type":["string","null"],"format":"date-time"},"delivered_at":{"type":["string","null"],"format":"date-time"},"read_at":{"type":["string","null"],"format":"date-time"},"error":{"type":["object","null"],"properties":{"code":{"type":["string","null"]},"message":{"type":"string"}},"required":["code","message"],"description":"Solo con `status: \"failed\"`: el motivo que dio el proveedor."},"interactive_reply":{"type":["object","null"],"properties":{"id":{"type":"string"},"title":{"type":"string"}},"required":["id","title"],"description":"Solo en entrantes que responden a botones o a una lista."},"flow_response":{"type":["object","null"],"properties":{"flow_token":{"type":["string","null"],"description":"El `flow_token` con que se mandó el Flow (el tuyo o el `hs_…` que generó heysteff)."},"data":{"type":"object","additionalProperties":{},"description":"Los campos que envió el cliente, con las claves de tu Flow JSON.","example":{"nombre":"Ana","fecha":"2026-11-20"}}},"required":["flow_token","data"],"description":"Solo en entrantes que responden a un WhatsApp Flow."}},"required":["id","conversation_id","channel","direction","status","content","content_type","external_message_id","created_at","sent_at","delivered_at","read_at","error","interactive_reply","flow_response"]},"MediaUploaded":{"type":"object","properties":{"data":{"type":"object","properties":{"url":{"type":"string","description":"Se manda tal cual en `media.url` de POST /v1/messages.","example":"/media/2c7e9b1a-3d4f-4a5b-8c6d-7e8f9a0b1c2d/9f2c41d0a7b3e815.jpg"},"mime_type":{"type":"string","example":"image/jpeg"},"filename":{"type":"string","example":"catalogo.jpg"},"size":{"type":"integer","example":184213}},"required":["url","mime_type","filename","size"]}},"required":["data"]},"Template":{"type":"object","properties":{"name":{"type":"string","example":"pedido_despachado"},"language":{"type":"string","example":"es"},"category":{"type":"string","example":"UTILITY"},"status":{"type":"string","example":"APPROVED"},"channel_connection_id":{"type":"string","format":"uuid"},"body":{"type":["string","null"],"example":"Hola {{1}}, tu pedido {{2}} va en camino."},"body_param_count":{"type":"integer","description":"Cuántos `template.body_params` hay que mandar en POST /v1/messages.","example":2},"header":{"type":["object","null"],"properties":{"format":{"type":"string"},"text":{"type":["string","null"]}},"required":["format","text"]},"footer":{"type":["string","null"]},"buttons":{"type":"array","items":{"type":"object","properties":{"type":{"type":"string"},"text":{"type":["string","null"]},"url":{"type":["string","null"]}},"required":["type","text","url"]}},"updated_at":{"type":"string","format":"date-time"}},"required":["name","language","category","status","channel_connection_id","body","body_param_count","header","footer","buttons","updated_at"]},"Conversation":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"channel":{"type":"string","example":"whatsapp_cloud"},"external_user_id":{"type":"string","description":"Identificador del cliente en el canal (teléfono, PSID, correo)."},"status":{"type":"string","example":"open"},"contact_id":{"type":["string","null"],"format":"uuid"},"contact_name":{"type":["string","null"]},"last_message_at":{"type":["string","null"],"format":"date-time"},"created_at":{"type":"string","format":"date-time"}},"required":["id","channel","external_user_id","status","contact_id","contact_name","last_message_at","created_at"]},"ConversationMessage":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"conversation_id":{"type":"string","format":"uuid"},"role":{"type":"string","description":"Quién escribió el mensaje.","example":"user"},"content":{"type":["string","null"]},"content_type":{"type":"string","example":"text"},"status":{"type":["string","null"],"example":"delivered"},"created_at":{"type":"string","format":"date-time"}},"required":["id","conversation_id","role","content","content_type","status","created_at"]},"ConversationDetail":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"channel":{"type":"string","description":"Canal. Va tal cual como `channel` de POST /v1/messages.","example":"whatsapp_cloud"},"channel_connection_id":{"type":["string","null"],"format":"uuid","description":"Número o cuenta por donde entró. Pásalo como `channel_connection_id` de POST /v1/messages para contestar por el mismo."},"external_user_id":{"type":"string","description":"A quién contestar: va como `to` de POST /v1/messages (teléfono, PSID, correo)."},"status":{"type":"string","example":"open"},"contact_id":{"type":["string","null"],"format":"uuid"},"contact_name":{"type":["string","null"]},"bot_paused":{"type":"boolean","description":"true = una persona tiene la conversación (o un flujo la lleva): tu IA no debería contestar."},"bot_paused_by":{"type":["string","null"],"description":"Quién pausó el bot: `api` (tu integración), `api_escalation` (la pasaste a una persona por la API), `human_agent` (alguien del equipo respondió o escaló), `human_deactivated`, `kanban_column`, `flow:<id>`, entre otros. null si no está pausado.","example":"api_escalation"},"bot_paused_until":{"type":["string","null"],"format":"date-time","description":"Cuándo vuelve solo el bot, si la pausa es temporal."},"assigned_user_id":{"type":["string","null"],"description":"Persona del equipo asignada, si hay."},"escalation":{"type":["object","null"],"properties":{"id":{"type":"string","format":"uuid"},"reason":{"type":"string","example":"api:handoff"},"urgency":{"type":["string","null"],"enum":["high","medium","low"],"description":"Se calcula unos segundos después de escalar, salvo que la mandes tú."},"taken":{"type":"boolean","description":"true cuando una persona ya la tomó."},"created_at":{"type":"string","format":"date-time"}},"required":["id","reason","urgency","taken","created_at"],"description":"Escalación abierta (una persona tiene que atenderla), o null."},"last_message_at":{"type":["string","null"],"format":"date-time"},"last_inbound_at":{"type":["string","null"],"format":"date-time","description":"Último mensaje del cliente. En WhatsApp Cloud el texto libre solo sale dentro de las 24 h siguientes."},"created_at":{"type":"string","format":"date-time"},"sandbox":{"type":"boolean","description":"Conversación del sandbox (key `sk_test_`)."}},"required":["id","channel","channel_connection_id","external_user_id","status","contact_id","contact_name","bot_paused","bot_paused_by","bot_paused_until","assigned_user_id","escalation","last_message_at","last_inbound_at","created_at","sandbox"]},"EscalateConversationRequest":{"type":"object","properties":{"reason":{"type":"string","minLength":1,"maxLength":300,"description":"Por qué va a una persona, en palabras. El equipo lo lee en la bandeja como un pendiente del caso.","example":"El cliente pide un reembolso y no tengo cómo aprobarlo."},"urgency":{"type":"string","enum":["high","medium","low"],"description":"Si la mandas, queda fija (no la reemplaza la clasificación automática)."}},"additionalProperties":false},"ConversationBotRequest":{"type":"object","properties":{"paused":{"type":"boolean","description":"true pausa el bot; false lo reanuda."}},"required":["paused"],"additionalProperties":false},"Contact":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"name":{"type":["string","null"]},"email":{"type":["string","null"]},"phone":{"type":["string","null"],"description":"Teléfono normalizado en formato E.164.","example":"+56912345678"},"avatar_url":{"type":["string","null"]},"external_ids":{"type":"object","additionalProperties":{}},"tags":{"type":"array","items":{"type":"string"}},"metadata":{"type":"object","additionalProperties":{}},"birth_month":{"type":["integer","null"]},"birth_day":{"type":["integer","null"]},"birth_year":{"type":["integer","null"]},"created_at":{"type":"string","format":"date-time"},"updated_at":{"type":"string","format":"date-time"}},"required":["id","name","email","phone","avatar_url","external_ids","tags","metadata","birth_month","birth_day","birth_year","created_at","updated_at"]},"ContactList":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"name":{"type":"string"},"source":{"type":"string","example":"csv"},"phone_region":{"type":["string","null"],"example":"CL"},"total_members":{"type":"integer"},"valid_members":{"type":"integer"},"created_at":{"type":"string","format":"date-time"}},"required":["id","name","source","phone_region","total_members","valid_members","created_at"]},"ContactListImportSummary":{"type":"object","additionalProperties":{}},"CampaignCreated":{"type":"object","properties":{"data":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"name":{"type":"string"},"status":{"type":"string"},"trigger_type":{"type":"string"},"channel":{"type":"string"},"triggered":{"type":"boolean","description":"true si la corrida se disparó en la misma solicitud."},"run_id":{"type":["string","null"]},"created_at":{"type":"string","format":"date-time"}},"required":["id","name","status","trigger_type","channel","triggered","run_id","created_at"]}},"required":["data"]},"Campaign":{"type":"object","properties":{"data":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"name":{"type":"string"},"status":{"type":"string"},"trigger_type":{"type":"string"},"channel":{"type":"string"},"message_mode":{"type":"string"},"recipients":{"type":"object","additionalProperties":{"type":"integer"},"description":"Destinatarios por estado de envío."},"created_at":{"type":"string","format":"date-time"},"updated_at":{"type":"string","format":"date-time"}},"required":["id","name","status","trigger_type","channel","message_mode","recipients","created_at","updated_at"]}},"required":["data"]},"DomainEvent":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"type":{"type":"string","example":"message.received"},"payload":{"type":"object","additionalProperties":{}},"occurred_at":{"type":"string","format":"date-time"},"seq":{"type":"string","description":"Posición en el stream (entero grande como string).","example":"18734"}},"required":["id","type","payload","occurred_at","seq"]},"Review":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"product_id":{"type":["string","null"]},"order_id":{"type":["string","null"]},"rating":{"type":"integer","minimum":1,"maximum":5},"title":{"type":["string","null"]},"body":{"type":["string","null"]},"attributes":{"type":["object","null"],"additionalProperties":{}},"author_name":{"type":["string","null"]},"verified":{"type":"boolean"},"source":{"type":"string"},"language":{"type":["string","null"]},"status":{"type":"string","example":"approved"},"reply_body":{"type":["string","null"]},"pinned":{"type":"boolean"},"featured":{"type":"boolean"},"helpful_count":{"type":"integer"},"sentiment":{"type":["string","null"]},"tags":{"type":["array","null"],"items":{"type":"string"}},"media":{"type":"array","items":{"type":"object","properties":{"type":{"type":"string"},"url":{"type":"string"},"thumb_url":{"type":["string","null"]}},"required":["type","url","thumb_url"]}},"created_at":{"type":"string","format":"date-time"}},"required":["id","product_id","order_id","rating","title","body","attributes","author_name","verified","source","language","status","reply_body","pinned","featured","helpful_count","sentiment","tags","media","created_at"]},"SalesFunnel":{"type":"object","additionalProperties":{}},"OwnerSleep":{"type":"object","properties":{"signal":{"type":["object","null"],"additionalProperties":{},"description":"Último aviso recibido, o null."},"signal_valid_until":{"type":["string","null"],"format":"date-time","description":"Hasta cuándo vale el aviso."},"asleep":{"type":"boolean"},"reason":{"type":"string"}},"required":["signal","signal_valid_until","asleep","reason"]},"WebhookBatch":{"type":["object","null"],"properties":{"wait_seconds":{"type":"integer","example":5},"max_size":{"type":"integer","example":50}},"required":["wait_seconds","max_size"],"description":"Agrupación de `message:received`; null = cada mensaje sale en su propio POST."},"Webhook":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"url":{"type":"string","example":"https://api.tutienda.com/heysteff/webhooks"},"events":{"type":"array","items":{"type":"string"},"example":["message.received","payment.paid"]},"format":{"type":"string","enum":["heysteff","meta"],"description":"`heysteff`: el sobre de heysteff firmado con `X-Heysteff-Signature`. `meta`: el payload crudo de la Cloud API de Meta firmado con `X-Hub-Signature-256` (ver la guía \"Compatible con Meta\")."},"is_active":{"type":"boolean"},"last_success_at":{"type":["string","null"],"format":"date-time"},"last_failure_at":{"type":["string","null"],"format":"date-time"},"failure_count":{"type":"integer","description":"Fallos seguidos desde el último 2xx."},"created_at":{"type":"string","format":"date-time"},"paused_at":{"type":["string","null"],"format":"date-time","description":"Cuándo se pausó solo por fallar demasiado (40 entregas o más en 15 minutos, con 85 % o más fallidas). Mientras tanto los eventos quedan en cola; salen al reactivarlo con `is_active: true`. null = no está en pausa."},"paused_reason":{"type":["string","null"],"example":"failure_rate"},"batch":{"$ref":"#/components/schemas/WebhookBatch"},"inactivity_hours":{"type":"integer","description":"Horas sin mensajes para `conversation.inactive`.","example":24}},"required":["id","url","events","format","is_active","last_success_at","last_failure_at","failure_count","created_at","paused_at","paused_reason","batch","inactivity_hours"],"description":"Webhook saliente. El secreto de firma nunca se devuelve: solo se ve al crearlo."},"WebhookDelivery":{"type":"object","properties":{"id":{"type":"string","format":"uuid","description":"Es el `X-Heysteff-Delivery-Id` que recibió tu servidor."},"webhook_id":{"type":"string","format":"uuid"},"event":{"type":"string","example":"message.received"},"state":{"type":"string","enum":["pending","retrying","succeeded","failed","skipped"],"description":"`pending`: aún no se intentó. `retrying`: el último intento falló y se va a reintentar sola. `succeeded`: tu servidor respondió 2xx. `failed`: agotó los reintentos automáticos. `skipped`: el evento no aplica a ese webhook (formato Meta y un evento sin forma Meta, como un mensaje borrado o de otro canal); no se mandó nada y no cuenta como fallo."},"status":{"type":"string","description":"Estado interno de la fila (`pending`, `success`, `failed`, `dead_letter`, `skipped`)."},"attempts":{"type":"integer"},"max_attempts":{"type":"integer","example":6},"http_status":{"type":["integer","null"],"description":"Status HTTP del último intento; null si no hubo respuesta.","example":500},"duration_ms":{"type":["integer","null"],"description":"Cuánto tardó tu servidor en el último intento.","example":842},"error":{"type":["string","null"],"enum":["http_status","timeout","destination_blocked","dns_failed","connection_refused","connection_reset","tls_error","network_error","internal_error","not_applicable"],"description":"Por qué falló el último intento. `http_status` = respondió algo distinto de 2xx; `not_applicable` = entrega omitida (`skipped`); el resto, que no se pudo conectar."},"response_body_excerpt":{"type":["string","null"],"description":"Hasta 500 caracteres de lo que respondió tu servidor. Solo cuando hubo una respuesta HTTP.","example":"{\"error\":\"invalid signature\"}"},"retryable":{"type":"boolean","description":"Si se puede reintentar con POST /v1/webhook-deliveries/{id}/retry."},"created_at":{"type":"string","format":"date-time"},"last_attempt_at":{"type":["string","null"],"format":"date-time"},"next_retry_at":{"type":["string","null"],"format":"date-time","description":"Próximo reintento automático."}},"required":["id","webhook_id","event","state","status","attempts","max_attempts","http_status","duration_ms","error","response_body_excerpt","retryable","created_at","last_attempt_at","next_retry_at"]},"WhatsAppNumber":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"phone_number":{"type":["string","null"],"description":"E.164. Por ahora solo números de EE. UU. (+1).","example":"+15550100199"},"country":{"type":"string","example":"US"},"state":{"type":"string","enum":["reserved","connected","disconnected"],"description":"`reserved`: reservado, esperando que alguien termine el alta en `connect_url` (vence en `reserved_until`). `connected`: en tu cuenta de WhatsApp con un canal activo. `disconnected`: sigue en tu cuenta, pero el canal de heysteff se desconectó."},"reserved_until":{"type":["string","null"],"format":"date-time"},"connected_at":{"type":["string","null"],"format":"date-time"},"channel_connection_id":{"type":["string","null"],"format":"uuid","description":"Canal de WhatsApp Cloud del número; úsalo como `channel_connection_id` en POST /v1/messages."},"display_name":{"type":"object","properties":{"name":{"type":["string","null"],"example":"Tienda Aurora"},"status":{"type":["string","null"],"description":"Lo que informa Meta: `PENDING_REVIEW`, `APPROVED`, `DECLINED`…","example":"PENDING_REVIEW"},"rejection_reason":{"type":["string","null"]},"requested_at":{"type":["string","null"],"format":"date-time"}},"required":["name","status","rejection_reason","requested_at"]}},"required":["id","phone_number","country","state","reserved_until","connected_at","channel_connection_id","display_name"],"description":"Número de WhatsApp de heysteff del workspace."},"WhatsAppNumberReservation":{"type":"object","properties":{"number":{"$ref":"#/components/schemas/WhatsAppNumber"},"connect_url":{"type":"string","description":"Página de heysteff que termina el alta en Meta con este número. De un solo uso.","example":"https://app.heysteff.com/connect/whatsapp?token=…&pool=1"},"connect_url_expires_at":{"type":"string","format":"date-time"},"reused":{"type":"boolean","description":"true = ya había una reserva viva: se reusó con plazo renovado y un link nuevo."}},"required":["number","connect_url","connect_url_expires_at","reused"]},"DisplayNameRequest":{"type":"object","properties":{"display_name":{"type":"string","minLength":3,"maxLength":50,"example":"Tienda Aurora"}},"required":["display_name"],"additionalProperties":false},"Customer":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"external_id":{"type":"string","description":"El id del cliente en tu sistema. Único en el workspace.","example":"acme-42"},"name":{"type":"string","example":"Acme SpA"},"metadata":{"type":"object","additionalProperties":{},"example":{"plan":"pro"}},"channels_count":{"type":"integer","description":"Canales activos que conectó este cliente."},"created_at":{"type":"string","format":"date-time"},"updated_at":{"type":"string","format":"date-time"}},"required":["id","external_id","name","metadata","channels_count","created_at","updated_at"],"description":"Cliente del workspace developer: el cliente final de tu SaaS."},"CustomerSetupLink":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"customer_id":{"type":"string","format":"uuid"},"allowed_channels":{"type":"array","items":{"type":"string","enum":["whatsapp_cloud","instagram","facebook","gmail"]}},"expires_at":{"type":"string","format":"date-time"},"used_at":{"type":["string","null"],"format":"date-time"},"channel_connection_id":{"type":["string","null"],"format":"uuid"},"success_redirect_url":{"type":["string","null"]},"failure_redirect_url":{"type":["string","null"]},"brand_name":{"type":["string","null"]},"brand_logo_url":{"type":["string","null"]},"locale":{"type":"string","enum":["auto","es","en","pt"],"description":"Idioma de la página; `auto` usa el del navegador."},"theme":{"type":"object","properties":{"primary_color":{"type":"string"},"background_color":{"type":"string"},"text_color":{"type":"string"},"border_color":{"type":"string"}},"description":"Colores de la página (#RRGGBB)."},"whatsapp_modes":{"type":"array","items":{"type":"string","enum":["own_number","coexistence","heysteff_number"]},"description":"`own_number`: un número que no está en la app de WhatsApp Business. `coexistence`: el número sigue en la app del celular y heysteff recibe los mismos chats. `heysteff_number`: un número de heysteff (si tu plan los incluye)."},"reconnect_channel_id":{"type":["string","null"],"format":"uuid","description":"Canal del cliente que este link vuelve a conectar."},"revoked_at":{"type":["string","null"],"format":"date-time"},"state":{"type":"string","enum":["active","used","expired","revoked"]},"created_at":{"type":"string","format":"date-time"},"url":{"type":"string","description":"Página de heysteff donde tu cliente conecta su canal. Solo viene al crear el link.","example":"https://developers.heysteff.com/connect/hsl_…"}},"required":["id","customer_id","allowed_channels","expires_at","used_at","channel_connection_id","success_redirect_url","failure_redirect_url","brand_name","brand_logo_url","locale","theme","whatsapp_modes","reconnect_channel_id","revoked_at","state","created_at","url"]},"CustomerSetupLinkStored":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"customer_id":{"type":"string","format":"uuid"},"allowed_channels":{"type":"array","items":{"type":"string","enum":["whatsapp_cloud","instagram","facebook","gmail"]}},"expires_at":{"type":"string","format":"date-time"},"used_at":{"type":["string","null"],"format":"date-time"},"channel_connection_id":{"type":["string","null"],"format":"uuid"},"success_redirect_url":{"type":["string","null"]},"failure_redirect_url":{"type":["string","null"]},"brand_name":{"type":["string","null"]},"brand_logo_url":{"type":["string","null"]},"locale":{"type":"string","enum":["auto","es","en","pt"],"description":"Idioma de la página; `auto` usa el del navegador."},"theme":{"type":"object","properties":{"primary_color":{"type":"string"},"background_color":{"type":"string"},"text_color":{"type":"string"},"border_color":{"type":"string"}},"description":"Colores de la página (#RRGGBB)."},"whatsapp_modes":{"type":"array","items":{"type":"string","enum":["own_number","coexistence","heysteff_number"]},"description":"`own_number`: un número que no está en la app de WhatsApp Business. `coexistence`: el número sigue en la app del celular y heysteff recibe los mismos chats. `heysteff_number`: un número de heysteff (si tu plan los incluye)."},"reconnect_channel_id":{"type":["string","null"],"format":"uuid","description":"Canal del cliente que este link vuelve a conectar."},"revoked_at":{"type":["string","null"],"format":"date-time"},"state":{"type":"string","enum":["active","used","expired","revoked"]},"created_at":{"type":"string","format":"date-time"}},"required":["id","customer_id","allowed_channels","expires_at","used_at","channel_connection_id","success_redirect_url","failure_redirect_url","brand_name","brand_logo_url","locale","theme","whatsapp_modes","reconnect_channel_id","revoked_at","state","created_at"]},"SetupLinkListItem":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"customer_id":{"type":"string","format":"uuid"},"allowed_channels":{"type":"array","items":{"type":"string","enum":["whatsapp_cloud","instagram","facebook","gmail"]}},"expires_at":{"type":"string","format":"date-time"},"used_at":{"type":["string","null"],"format":"date-time"},"channel_connection_id":{"type":["string","null"],"format":"uuid"},"success_redirect_url":{"type":["string","null"]},"failure_redirect_url":{"type":["string","null"]},"brand_name":{"type":["string","null"]},"brand_logo_url":{"type":["string","null"]},"locale":{"type":"string","enum":["auto","es","en","pt"],"description":"Idioma de la página; `auto` usa el del navegador."},"theme":{"type":"object","properties":{"primary_color":{"type":"string"},"background_color":{"type":"string"},"text_color":{"type":"string"},"border_color":{"type":"string"}},"description":"Colores de la página (#RRGGBB)."},"whatsapp_modes":{"type":"array","items":{"type":"string","enum":["own_number","coexistence","heysteff_number"]},"description":"`own_number`: un número que no está en la app de WhatsApp Business. `coexistence`: el número sigue en la app del celular y heysteff recibe los mismos chats. `heysteff_number`: un número de heysteff (si tu plan los incluye)."},"reconnect_channel_id":{"type":["string","null"],"format":"uuid","description":"Canal del cliente que este link vuelve a conectar."},"revoked_at":{"type":["string","null"],"format":"date-time"},"state":{"type":"string","enum":["active","used","expired","revoked"]},"created_at":{"type":"string","format":"date-time"},"customer_name":{"type":"string"},"customer_external_id":{"type":"string"}},"required":["id","customer_id","allowed_channels","expires_at","used_at","channel_connection_id","success_redirect_url","failure_redirect_url","brand_name","brand_logo_url","locale","theme","whatsapp_modes","reconnect_channel_id","revoked_at","state","created_at","customer_name","customer_external_id"]},"CustomerUsage":{"type":"object","properties":{"customer_id":{"type":"string","format":"uuid"},"from":{"type":"string","example":"2026-11-01"},"to":{"type":"string","example":"2026-11-30"},"totals":{"type":"object","properties":{"messages":{"type":"integer","description":"Entrantes + salientes."},"inbound":{"type":"integer"},"outbound":{"type":"integer","description":"Lo que salió o intentó salir, fallidos incluidos."},"failed":{"type":"integer"},"conversations":{"type":"integer"}},"required":["messages","inbound","outbound","failed","conversations"]},"by_channel":{"type":"array","items":{"type":"object","properties":{"channel":{"type":"string","example":"whatsapp_cloud"},"messages":{"type":"integer","description":"Entrantes + salientes."},"inbound":{"type":"integer"},"outbound":{"type":"integer","description":"Lo que salió o intentó salir, fallidos incluidos."},"failed":{"type":"integer"},"conversations":{"type":"integer"}},"required":["channel","messages","inbound","outbound","failed","conversations"]}},"by_day":{"type":"array","items":{"type":"object","properties":{"date":{"type":"string","example":"2026-11-14"},"messages":{"type":"integer","description":"Entrantes + salientes."},"inbound":{"type":"integer"},"outbound":{"type":"integer","description":"Lo que salió o intentó salir, fallidos incluidos."},"failed":{"type":"integer"}},"required":["date","messages","inbound","outbound","failed"]}}},"required":["customer_id","from","to","totals","by_channel","by_day"]},"CustomerChannel":{"type":"object","properties":{"id":{"type":"string","format":"uuid","description":"Úsalo como `channel_connection_id` en POST /v1/messages."},"channel_type":{"type":"string","example":"whatsapp_cloud"},"display_name":{"type":["string","null"]},"provider_account_id":{"type":"string"},"is_active":{"type":"boolean"},"connected_at":{"type":["string","null"],"format":"date-time"}},"required":["id","channel_type","display_name","provider_account_id","is_active","connected_at"]},"WhatsAppFlowValidationError":{"type":"object","properties":{"error":{"type":"string","example":"INVALID_PROPERTY_VALUE"},"error_type":{"type":"string","example":"FLOW_JSON_ERROR"},"message":{"type":"string"},"line_start":{"type":"integer"},"line_end":{"type":"integer"},"column_start":{"type":"integer"},"column_end":{"type":"integer"},"pointers":{"type":"array","items":{"type":"object","additionalProperties":{}}}},"required":["error","error_type","message"],"description":"Un error del Flow JSON tal como lo devuelve Meta."},"WhatsAppFlow":{"type":"object","properties":{"id":{"type":"string","format":"uuid","description":"Id del Flow en heysteff (el de las rutas `/v1/whatsapp-flows/{id}`)."},"flow_id":{"type":"string","description":"Id del Flow en Meta: va en `flow.flow_id` de POST /v1/messages y en el botón FLOW de una plantilla.","example":"1234567890123456"},"channel_connection_id":{"type":"string","format":"uuid","description":"Número de WhatsApp Cloud cuya cuenta (WABA) tiene el Flow."},"name":{"type":"string","example":"Agendar visita"},"status":{"type":"string","enum":["DRAFT","PUBLISHED","DEPRECATED","BLOCKED","THROTTLED"],"description":"`DRAFT` (se edita y se prueba con `mode: \"draft\"`), `PUBLISHED` (se manda a clientes; ya no cambia), `DEPRECATED` (ya no se manda ni se abre), `BLOCKED` o `THROTTLED` (Meta lo limitó por su salud)."},"categories":{"type":"array","items":{"type":"string","enum":["SIGN_UP","SIGN_IN","APPOINTMENT_BOOKING","LEAD_GENERATION","CONTACT_US","CUSTOMER_SUPPORT","SURVEY","OTHER"]}},"validation_errors":{"type":"array","items":{"$ref":"#/components/schemas/WhatsAppFlowValidationError"},"description":"Vacío = el Flow JSON está listo para publicar."},"json_version":{"type":["string","null"],"example":"6.3"},"has_flow_json":{"type":"boolean","description":"Si heysteff guarda un Flow JSON subido desde la API o la consola."},"save_to_contact":{"type":"boolean","description":"Si cada campo simple de una respuesta se guarda en el campo del contacto con la misma clave."},"preview_url":{"type":["string","null"],"description":"Último link de vista previa pedido (GET /v1/whatsapp-flows/{id}/preview)."},"preview_expires_at":{"type":["string","null"],"format":"date-time"},"responses_count":{"type":"integer"},"last_synced_at":{"type":["string","null"],"format":"date-time"},"created_at":{"type":"string","format":"date-time"},"updated_at":{"type":"string","format":"date-time"}},"required":["id","flow_id","channel_connection_id","name","status","categories","validation_errors","json_version","has_flow_json","save_to_contact","preview_url","preview_expires_at","responses_count","last_synced_at","created_at","updated_at"],"description":"Un WhatsApp Flow (formulario dentro del chat) de la cuenta de WhatsApp del espacio."},"WhatsAppFlowDetail":{"allOf":[{"$ref":"#/components/schemas/WhatsAppFlow"},{"type":"object","properties":{"flow_json":{"type":["string","null"],"description":"El último Flow JSON subido desde heysteff, como texto. Para el objeto (o el que tiene Meta si se creó en WhatsApp Manager), GET /v1/whatsapp-flows/{id}/json."}},"required":["flow_json"]}],"description":"Un WhatsApp Flow (formulario dentro del chat) de la cuenta de WhatsApp del espacio."},"WhatsAppFlowResponse":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"flow_row_id":{"type":["string","null"],"format":"uuid","description":"Id heysteff del Flow; null si la respuesta no se pudo atar a un Flow conocido."},"flow_id":{"type":["string","null"],"description":"Id del Flow en Meta."},"flow_name":{"type":["string","null"]},"conversation_id":{"type":["string","null"],"format":"uuid"},"contact_id":{"type":["string","null"],"format":"uuid"},"contact_name":{"type":["string","null"]},"message_id":{"type":["string","null"],"format":"uuid","description":"El mensaje entrante con la respuesta (el mismo de `message:received`)."},"flow_token":{"type":["string","null"],"description":"El `flow_token` con que se mandó el Flow."},"data":{"type":"object","additionalProperties":{},"example":{"nombre":"Ana","fecha":"2026-11-20","servicio":"corte"}},"saved_to_contact":{"type":"boolean"},"created_at":{"type":"string","format":"date-time"}},"required":["id","flow_row_id","flow_id","flow_name","conversation_id","contact_id","contact_name","message_id","flow_token","data","saved_to_contact","created_at"]},"WhatsAppCall":{"type":"object","properties":{"id":{"type":"string","format":"uuid","description":"Id de la llamada en heysteff (`call_id` de los webhooks `call.*`)."},"wa_call_id":{"type":"string","example":"wacid.HBgLMTU1NTAxMDAxOTkVAgARGBI…"},"channel_connection_id":{"type":"string","format":"uuid"},"conversation_id":{"type":["string","null"],"format":"uuid"},"contact_id":{"type":["string","null"],"format":"uuid"},"contact_name":{"type":["string","null"]},"direction":{"type":"string","enum":["inbound","outbound"]},"status":{"type":"string","enum":["ringing","answered","completed","missed","rejected","failed"],"description":"`ringing` (sonando), `answered` (en curso), `completed` (terminó después de contestar), `missed` (nadie contestó), `rejected` (la persona la rechazó) o `failed` (error de Meta)."},"customer":{"type":["string","null"],"description":"Teléfono (o id de usuario) del cliente.","example":"56912345678"},"business_number":{"type":["string","null"],"example":"+1 555 010 0199"},"started_at":{"type":["string","null"],"format":"date-time"},"ended_at":{"type":["string","null"],"format":"date-time"},"duration_seconds":{"type":["integer","null"]},"error":{"type":["object","null"],"properties":{"code":{"type":["integer","null"]},"message":{"type":"string"}},"required":["code","message"]},"created_at":{"type":"string","format":"date-time"},"updated_at":{"type":"string","format":"date-time"}},"required":["id","wa_call_id","channel_connection_id","conversation_id","contact_id","contact_name","direction","status","customer","business_number","started_at","ended_at","duration_seconds","error","created_at","updated_at"]},"WhatsAppCallingSettings":{"type":"object","properties":{"channel_connection_id":{"type":"string","format":"uuid"},"enabled":{"type":"boolean"},"call_icon_visible":{"type":"boolean"},"callback_permission":{"type":"boolean"},"call_hours":{"type":["object","null"],"properties":{"timezone_id":{"type":"string","example":"America/Santiago"},"weekly":{"type":"array","items":{"type":"object","properties":{"day":{"type":"string","enum":["MONDAY","TUESDAY","WEDNESDAY","THURSDAY","FRIDAY","SATURDAY","SUNDAY"]},"open":{"type":"string","example":"0900"},"close":{"type":"string","example":"1800"}},"required":["day","open","close"]}},"holidays":{"type":"array","items":{"type":"object","properties":{"date":{"type":"string","example":"2026-12-25"},"start":{"type":"string"},"end":{"type":"string"}},"required":["date","start","end"]}}},"required":["timezone_id","weekly"],"description":"null = sin horario (se puede llamar siempre)."},"sip_enabled":{"type":"boolean","description":"Si el número tiene SIP encendido en Meta. Con SIP, Meta manda las llamadas a tu servidor SIP y deja de mandar los webhooks de llamadas: heysteff no las registra."}},"required":["channel_connection_id","enabled","call_icon_visible","callback_permission","call_hours","sip_enabled"]},"WhatsAppNumberStatus":{"type":"object","properties":{"channel_connection_id":{"type":"string","format":"uuid"},"phone_number_id":{"type":"string"},"waba_id":{"type":"string"},"health":{"type":["object","null"],"properties":{"display_phone_number":{"type":["string","null"]},"verified_name":{"type":["string","null"]},"quality_rating":{"type":["string","null"],"description":"`GREEN`, `YELLOW`, `RED` o `UNKNOWN`."},"messaging_limit_tier":{"type":["string","null"],"description":"Conversaciones que inicia el negocio por día: `TIER_250`, `TIER_1K`, `TIER_10K`…","example":"TIER_1K"},"name_status":{"type":["string","null"]},"code_verification_status":{"type":["string","null"]},"status":{"type":["string","null"],"description":"`CONNECTED`, `FLAGGED`, `RESTRICTED`…"},"throughput_level":{"type":["string","null"]}},"required":["display_phone_number","verified_name","quality_rating","messaging_limit_tier","name_status","code_verification_status","status","throughput_level"],"description":"null si Meta no respondió (el motivo va en `errors`)."},"display_name":{"type":"object","properties":{"name":{"type":["string","null"]},"status":{"type":["string","null"],"description":"`APPROVED`, `PENDING_REVIEW`, `DECLINED`…"},"requested_name":{"type":["string","null"]},"rejection_reason":{"type":["string","null"]}},"required":["name","status","requested_name","rejection_reason"]},"business_verification":{"type":["object","null"],"properties":{"business_verification_status":{"type":["string","null"],"description":"`verified`, `unverified`, `pending`, `rejected`… (en minúsculas)."},"account_review_status":{"type":["string","null"],"description":"Revisión de la cuenta de WhatsApp: `APPROVED`, `PENDING` o `REJECTED`."},"waba_name":{"type":["string","null"]},"verify_url":{"type":"string","description":"Centro de seguridad de Meta Business, donde se verifica la empresa."}},"required":["business_verification_status","account_review_status","waba_name","verify_url"]},"errors":{"type":"array","items":{"type":"string"},"description":"Partes que Meta no devolvió; el resto de la respuesta sigue siendo válido."}},"required":["channel_connection_id","phone_number_id","waba_id","health","display_name","business_verification","errors"]},"WhatsAppBusinessProfile":{"type":"object","properties":{"channel_connection_id":{"type":"string","format":"uuid"},"about":{"type":["string","null"],"description":"El \"Info\" del perfil (hasta 139 caracteres)."},"description":{"type":["string","null"]},"address":{"type":["string","null"]},"email":{"type":["string","null"]},"websites":{"type":"array","items":{"type":"string"}},"vertical":{"anyOf":[{"type":"string","enum":["UNDEFINED","OTHER","AUTO","BEAUTY","APPAREL","EDU","ENTERTAIN","EVENT_PLAN","FINANCE","GROCERY","GOVT","HOTEL","HEALTH","NONPROFIT","PROF_SERVICES","RETAIL","TRAVEL","RESTAURANT","ALCOHOL","ONLINE_GAMBLING","PHYSICAL_GAMBLING","OTC_DRUGS","NOT_A_BIZ"]},{"type":"string"},{"type":"null"}]},"profile_picture_url":{"type":["string","null"]}},"required":["channel_connection_id","about","description","address","email","websites","vertical","profile_picture_url"]},"AgentTrialQuota":{"type":"object","properties":{"period":{"type":"string","description":"Mes UTC del cupo.","example":"2026-11"},"used":{"type":"integer"},"limit":{"type":["integer","null"],"description":"Respuestas incluidas al mes con la key de la plataforma. null = sin tope."},"remaining":{"type":["integer","null"]}},"required":["period","used","limit","remaining"]},"Agent":{"type":"object","properties":{"enabled":{"type":"boolean"},"instructions":{"type":"string","description":"System prompt del agente."},"provider":{"type":["string","null"],"enum":["anthropic","openai","deepseek","openrouter"],"description":"null = el modelo por defecto de la plataforma."},"model":{"type":["string","null"],"example":"claude-haiku-5-5"},"temperature":{"type":"number","description":"0 = preciso, 1 = creativo."},"keys":{"type":"array","items":{"type":"object","properties":{"provider":{"type":"string"},"last4":{"type":"string"},"updated_at":{"type":"string"}},"required":["provider","last4","updated_at"]},"description":"Keys propias guardadas: solo el proveedor y los últimos 4."},"quota":{"$ref":"#/components/schemas/AgentTrialQuota"},"channels":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"channel_type":{"type":"string"},"display_name":{"type":["string","null"]},"developer_customer_id":{"type":["string","null"],"format":"uuid"},"agent_enabled":{"type":["boolean","null"],"description":"true/false fija; null hereda del cliente o del espacio."}},"required":["id","channel_type","display_name","developer_customer_id","agent_enabled"]}},"customers":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"external_id":{"type":"string"},"name":{"type":"string"},"agent_enabled":{"type":["boolean","null"]}},"required":["id","external_id","name","agent_enabled"]}},"models":{"type":"array","items":{"type":"object","properties":{"provider":{"type":"string","enum":["anthropic","openai","deepseek","openrouter"]},"model":{"type":"string"},"label":{"type":"string"},"input_usd_per_mtok":{"type":["number","null"]},"output_usd_per_mtok":{"type":["number","null"]}},"required":["provider","model","label","input_usd_per_mtok","output_usd_per_mtok"]},"description":"Modelos sugeridos con su precio de lista por millón de tokens (null si no hay tarifa)."}},"required":["enabled","instructions","provider","model","temperature","keys","quota","channels","customers","models"],"description":"El agente de IA del workspace developer."},"AgentTool":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"slug":{"type":"string"},"name":{"type":"string"},"description":{"type":["string","null"]},"mode":{"type":"string"},"spec":{"type":"object","additionalProperties":{}},"auth_kind":{"type":"string"},"is_active":{"type":"boolean"},"product_catalog":{"type":"boolean"},"created_at":{"type":"string"},"updated_at":{"type":"string"}},"required":["id","slug","name","description","mode","spec","auth_kind","is_active","product_catalog","created_at","updated_at"],"description":"Custom tool: una llamada HTTP a tu backend que el agente hace durante una conversación."},"InboxEmbed":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"name":{"type":"string"},"scope":{"type":"string","enum":["all","channel","customer"]},"channel_connection_id":{"type":["string","null"],"format":"uuid"},"customer_id":{"type":["string","null"],"format":"uuid"},"allowed_origins":{"type":"array","items":{"type":"string"}},"theme":{"type":"string","enum":["system","light","dark"]},"locale":{"type":"string","enum":["auto","es","en","pt"]},"expires_at":{"type":["string","null"],"format":"date-time"},"revoked_at":{"type":["string","null"],"format":"date-time"},"last_used_at":{"type":["string","null"],"format":"date-time"},"state":{"type":"string","enum":["active","expired","revoked"]},"created_at":{"type":"string","format":"date-time"}},"required":["id","name","scope","channel_connection_id","customer_id","allowed_origins","theme","locale","expires_at","revoked_at","last_used_at","state","created_at"]},"InboxEmbedCreated":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"name":{"type":"string"},"scope":{"type":"string","enum":["all","channel","customer"]},"channel_connection_id":{"type":["string","null"],"format":"uuid"},"customer_id":{"type":["string","null"],"format":"uuid"},"allowed_origins":{"type":"array","items":{"type":"string"}},"theme":{"type":"string","enum":["system","light","dark"]},"locale":{"type":"string","enum":["auto","es","en","pt"]},"expires_at":{"type":["string","null"],"format":"date-time"},"revoked_at":{"type":["string","null"],"format":"date-time"},"last_used_at":{"type":["string","null"],"format":"date-time"},"state":{"type":"string","enum":["active","expired","revoked"]},"created_at":{"type":"string","format":"date-time"},"token":{"type":"string","description":"Solo viene en esta respuesta.","example":"hse_…"},"embed_url":{"type":"string","example":"https://developers.heysteff.com/embed/inbox?token=hse_…"},"snippet":{"type":"string","description":"El iframe listo para pegar en tu producto."}},"required":["id","name","scope","channel_connection_id","customer_id","allowed_origins","theme","locale","expires_at","revoked_at","last_used_at","state","created_at","token","embed_url","snippet"]},"LogEntry":{"type":"object","properties":{"id":{"type":"string","example":"webhooks:6f1c…"},"source":{"type":"string","enum":["api","webhooks","messages"]},"at":{"type":"string","format":"date-time"},"ok":{"type":"boolean"},"status":{"type":["integer","null"],"description":"Status HTTP (API y webhooks); null en mensajes o sin respuesta."},"title":{"type":"string","description":"Método HTTP, evento o tipo de canal.","example":"POST"},"detail":{"type":"string","description":"Ruta, URL del webhook o error del mensaje.","example":"/api/v1/messages"},"duration_ms":{"type":["integer","null"]},"error":{"type":["string","null"],"description":"Motivo del fallo (webhooks) o clase del error (mensajes)."},"ref":{"type":"object","properties":{"request_id":{"type":"string"},"api_key_id":{"type":["string","null"]},"delivery_id":{"type":"string"},"webhook_id":{"type":"string"},"message_id":{"type":"string"},"conversation_id":{"type":"string"}}}},"required":["id","source","at","ok","status","title","detail","duration_ms","error","ref"]}},"parameters":{}},"paths":{"/v1/messages":{"post":{"operationId":"createMessage","tags":["Mensajes"],"summary":"Enviar un mensaje","description":"Encola un mensaje a un contacto por el canal indicado. Además de texto, media y plantillas: `content_type: \"interactive\"` (botones o lista en `interactive`; nativos en WhatsApp Cloud, quick replies en Instagram y Messenger, texto con opciones numeradas en SMS, email y WhatsApp por QR, con `degraded: true` en la respuesta; la respuesta del cliente llega en `message:received` con `interactive_reply: { id, title }`), `content_type: \"location\"` (nativa en WhatsApp Cloud; en el resto, texto con un enlace de mapa) y `content_type: \"reaction\"` (un emoji sobre `reaction.message_id`, en WhatsApp e Instagram, responde 202; Instagram solo acepta ❤️ y cualquier otro emoji responde 422 `reaction_emoji_unsupported`; Messenger responde 422 `reactions_unsupported`; una key de prueba responde 403 `test_key_not_allowed`). Botones, listas y ubicaciones exigen la ventana de 24 h abierta en WhatsApp Cloud, Instagram y Messenger (409 `wa_window_closed` / `meta_window_closed`). Un texto más largo que el canal (Instagram 1.000, Messenger 2.000) sale en piezas (`parts`). Fuera de la ventana de 24 h de WhatsApp Cloud solo se pueden mandar plantillas aprobadas (`content_type: \"template\"`). Para mandar una imagen, audio, video o documento, sube el archivo con POST /v1/media y manda su `url` en `media.url` con `content_type` `image`, `audio`, `video` o `file`; `content` va como pie (opcional). Las plantillas disponibles salen de GET /v1/templates. Sin `channel_connection_id` sale por el canal predeterminado de la API del workspace. Con `customer_id` (un cliente de POST /v1/customers) sale por el canal de ese cliente del tipo `channel`, el conectado más reciente con su link de conexión; si no tiene uno activo responde 422 `customer_channel_not_connected`. Con `channel: \"sms\"` solo se admite texto (`content_type: \"text\"`, hasta 1.600 caracteres) a un teléfono en formato internacional; si no, responde 422 `invalid_sms_message`, y un `channel_connection_id` que no es de un número SMS responde 422 `sms_sender_mismatch`. El SMS se cobra por segmento y cada mensaje admite hasta 3. Solo en WhatsApp Cloud (`channel: \"whatsapp_cloud\"`; en otro canal, o si el remitente no es un número de WhatsApp Cloud, responde 422 `whatsapp_cloud_only`, sin degradarse a texto ni a un link) y solo con una key `sk_live_` (una key de prueba responde 403 `test_key_not_allowed`): `content_type: \"flow\"` manda un WhatsApp Flow (formulario dentro del chat) con `flow: { flow_id, cta, body, header?, footer?, flow_token?, mode?, action?, screen?, data? }`; `flow_id` es el id de Meta que da GET /v1/whatsapp-flows; si falta `flow_token`, heysteff genera uno `hs_…`; `mode: \"draft\"` sirve para probar un borrador. La respuesta del cliente llega en `message:received` con `flow_response: { flow_token, data }`. `content_type: \"voice_call\"` manda un botón para llamar al negocio por WhatsApp (`voice_call: { body, display_text?, ttl_minutes?, payload? }`; el número necesita las llamadas encendidas con PATCH /v1/whatsapp-numbers/{id}/calling) y `content_type: \"call_permission_request\"` le pide al cliente permiso para llamarlo (`call_permission: { body }`). Los tres son mensajes de sesión: fuera de la ventana de 24 h responden 409 `wa_window_closed` (fuera de la ventana, usa una plantilla con botón FLOW o de llamada). Si Meta rechaza uno al enviarlo, el mensaje queda `failed` (webhook `message.failed`) y no se reemplaza por texto.","security":[{"bearerAuth":[]}],"x-heysteff-scopes":["messages:write"],"parameters":[{"schema":{"type":"string","minLength":1,"maxLength":256,"description":"Clave única por operación lógica. Si repites la solicitud con la misma clave, recibes exactamente la misma respuesta que la primera vez, sin efectos duplicados.","example":"pedido-1042-confirmacion"},"required":false,"name":"Idempotency-Key","in":"header"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"to":{"type":"string","minLength":1,"maxLength":256},"contact_ref":{"type":"string","minLength":1,"maxLength":256},"channel":{"type":"string","enum":["instagram","facebook","whatsapp_cloud","whatsapp_baileys","gmail","sms","shopify"]},"channel_connection_id":{"type":"string","format":"uuid"},"customer_id":{"type":"string","format":"uuid"},"content":{"type":"string","minLength":1,"maxLength":4096},"content_type":{"type":"string","enum":["text","image","audio","video","file","template","interactive","reaction","location","flow","voice_call","call_permission_request"],"default":"text"},"template":{"type":"object","properties":{"name":{"type":"string","minLength":1,"maxLength":512},"language":{"type":"string","minLength":2,"maxLength":16},"body_params":{"type":"array","items":{"type":"string","maxLength":1024},"maxItems":50},"components":{"type":"array","items":{"type":"object","additionalProperties":{}},"maxItems":20}},"required":["name","language"]},"media":{"type":"object","properties":{"url":{"type":"string","minLength":1,"maxLength":2048},"filename":{"type":"string","minLength":1,"maxLength":255}},"required":["url"]},"interactive":{"oneOf":[{"type":"object","properties":{"type":{"type":"string","enum":["buttons"]},"body":{"type":"string","minLength":1,"maxLength":1024},"buttons":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","minLength":1,"maxLength":200},"title":{"type":"string","minLength":1,"maxLength":20}},"required":["id","title"]},"minItems":1,"maxItems":3}},"required":["type","body","buttons"]},{"type":"object","properties":{"type":{"type":"string","enum":["list"]},"body":{"type":"string","minLength":1,"maxLength":1024},"button":{"type":"string","minLength":1,"maxLength":20},"sections":{"type":"array","items":{"type":"object","properties":{"title":{"type":"string","maxLength":24},"rows":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","minLength":1,"maxLength":200},"title":{"type":"string","minLength":1,"maxLength":24},"description":{"type":"string","maxLength":72}},"required":["id","title"]},"minItems":1,"maxItems":30}},"required":["rows"]},"minItems":1,"maxItems":10}},"required":["type","body","button","sections"]}]},"reaction":{"type":"object","properties":{"message_id":{"type":"string","format":"uuid"},"emoji":{"type":"string","maxLength":32}},"required":["message_id","emoji"]},"location":{"type":"object","properties":{"latitude":{"type":"number","minimum":-90,"maximum":90},"longitude":{"type":"number","minimum":-180,"maximum":180},"name":{"type":"string","minLength":1,"maxLength":256},"address":{"type":"string","minLength":1,"maxLength":512}},"required":["latitude","longitude"]},"flow":{"type":"object","properties":{"flow_id":{"type":"string","minLength":1,"maxLength":64,"pattern":"^\\d+$"},"cta":{"type":"string","minLength":1,"maxLength":30},"body":{"type":"string","minLength":1,"maxLength":1024},"header":{"type":"string","minLength":1,"maxLength":60},"footer":{"type":"string","minLength":1,"maxLength":60},"flow_token":{"type":"string","minLength":1,"maxLength":512},"mode":{"type":"string","enum":["draft","published"],"default":"published"},"action":{"type":"string","enum":["navigate","data_exchange"],"default":"navigate"},"screen":{"type":"string","minLength":1,"maxLength":100},"data":{"type":"object","additionalProperties":{}}},"required":["flow_id","cta","body"]},"voice_call":{"type":"object","properties":{"body":{"type":"string","minLength":1,"maxLength":1024},"display_text":{"type":"string","minLength":1,"maxLength":20},"ttl_minutes":{"type":"integer","minimum":1,"maximum":43200},"payload":{"type":"string","maxLength":512}},"required":["body"]},"call_permission":{"type":"object","properties":{"body":{"type":"string","minLength":1,"maxLength":1024}},"required":["body"]}},"required":["channel"]}}}},"responses":{"201":{"description":"Mensaje encolado para envío.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/MessageCreated"}}}},"202":{"description":"Reacción en cola (`content_type: \"reaction\"`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ReactionAccepted"}}}},"400":{"description":"El cuerpo o los parámetros no pasan la validación (`validation_error`, con el detalle por campo en `details`) o el JSON es inválido (`invalid_json`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"Falta la API key, es inválida, venció o fue revocada (`unauthorized`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"402":{"description":"Se alcanzó un límite del plan (`plan_limit_exceeded`, con `upgrade_url` en `details`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"La API key no tiene el scope requerido, la IP no está permitida o el workspace está deshabilitado.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"409":{"description":"Ya hay una solicitud con la misma `Idempotency-Key` en proceso (`idempotency_conflict`), o botones, listas, ubicaciones, Flows, botones de llamar y pedidos de permiso fuera de la ventana de 24 h (`wa_window_closed`, `meta_window_closed`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"422":{"description":"La solicitud es válida pero no se puede procesar: por ejemplo, no hay un canal conectado para enviar, `whatsapp_cloud_only` (un Flow, un botón de llamar o un pedido de permiso fuera de WhatsApp Cloud), `customer_channel_not_connected`, `invalid_sms_message` o `sms_sender_mismatch`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"429":{"description":"Se superó el límite de solicitudes de la API key (`rate_limited`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/v1/messages/{id}":{"get":{"operationId":"getMessage","tags":["Mensajes"],"summary":"Estado de un mensaje","description":"Un mensaje del workspace con su estado (con una key de prueba, solo los del sandbox) (`queued`, `sent`, `delivered`, `read`, `failed`; `received` si lo mandó el cliente), el canal, los momentos de envío, entrega y lectura, y el error del proveedor si falló; un entrante que responde a un WhatsApp Flow trae `flow_response`. Los cambios de estado también llegan por webhook (`message.delivered`, `message.read`, `message.failed`).","security":[{"bearerAuth":[]}],"x-heysteff-scopes":["conversations:read"],"parameters":[{"schema":{"type":"string","format":"uuid","example":"6f1c2a8e-3b4d-4e5f-9a0b-1c2d3e4f5a6b"},"required":true,"name":"id","in":"path"}],"responses":{"200":{"description":"El mensaje.","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/Message"}},"required":["data"]}}}},"400":{"description":"El id no es un UUID (`invalid_id`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"Falta la API key, es inválida, venció o fue revocada (`unauthorized`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"La API key no tiene el scope requerido, la IP no está permitida o el workspace está deshabilitado.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"El recurso no existe en este workspace (`not_found`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"429":{"description":"Se superó el límite de solicitudes de la API key (`rate_limited`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/v1/messages/{id}/read":{"post":{"operationId":"markMessageRead","tags":["Mensajes"],"summary":"Marcar como leído","description":"Marca como leído un mensaje que mandó el cliente: la conversación queda leída en heysteff y, donde el canal lo permite, el cliente lo ve (tildes azules en WhatsApp Cloud; \"visto\" en Instagram y Messenger, donde Meta marca la conversación entera). SMS, email y WhatsApp por QR no tienen recibo: responde `supported: false` sin mandar nada. `receipt_sent` dice si el proveedor lo aceptó.","security":[{"bearerAuth":[]}],"x-heysteff-scopes":["messages:write"],"parameters":[{"schema":{"type":"string","format":"uuid","example":"6f1c2a8e-3b4d-4e5f-9a0b-1c2d3e4f5a6b"},"required":true,"name":"id","in":"path"}],"responses":{"200":{"description":"Marcado.","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"message_id":{"type":"string","format":"uuid"},"conversation_id":{"type":"string","format":"uuid"},"channel":{"type":"string"},"supported":{"type":"boolean"},"receipt_sent":{"type":"boolean"}},"required":["message_id","conversation_id","channel","supported","receipt_sent"]}},"required":["data"]}}}},"400":{"description":"El id no es un UUID (`invalid_id`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"Falta la API key, es inválida, venció o fue revocada (`unauthorized`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"La API key no tiene el scope requerido, la IP no está permitida o el workspace está deshabilitado.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"El recurso no existe en este workspace (`not_found`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"422":{"description":"El mensaje no lo mandó el cliente (`not_inbound`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"429":{"description":"Se superó el límite de solicitudes de la API key (`rate_limited`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/v1/media":{"post":{"operationId":"uploadMedia","tags":["Mensajes"],"summary":"Subir un archivo","description":"Sube un archivo (multipart, campo `file`) para mandarlo después en un mensaje: imágenes (JPEG, PNG, WebP, GIF), audio (MP3, M4A, OGG, WAV, WebM), video (MP4, MOV), PDF y documentos de oficina, hasta 15 MB. La `url` que devuelve va en `media.url` de POST /v1/messages. Hasta 30 subidas por minuto y 1.000 por día por workspace.","security":[{"bearerAuth":[]}],"x-heysteff-scopes":["messages:write"],"requestBody":{"required":true,"content":{"multipart/form-data":{"schema":{"type":"object","properties":{"file":{"type":"string","format":"binary"}},"required":["file"]}}}},"responses":{"201":{"description":"Archivo guardado.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/MediaUploaded"}}}},"400":{"description":"El cuerpo o los parámetros no pasan la validación (`validation_error`, con el detalle por campo en `details`) o el JSON es inválido (`invalid_json`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"Falta la API key, es inválida, venció o fue revocada (`unauthorized`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"La API key no tiene el scope requerido, la IP no está permitida o el workspace está deshabilitado.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"413":{"description":"El archivo pesa más de 15 MB.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"415":{"description":"Tipo de archivo no permitido (SVG, HTML y texto nunca se aceptan).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"429":{"description":"Se superó el límite de solicitudes de la API key (`rate_limited`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/v1/templates":{"get":{"operationId":"listTemplates","tags":["Mensajes"],"summary":"Listar plantillas de WhatsApp","description":"Plantillas de WhatsApp Cloud de los números conectados del workspace, con lo necesario para mandarlas por POST /v1/messages (`content_type: \"template\"`). Por defecto solo las aprobadas; `status=all` trae todas. Con una key de prueba la lista viene vacía: el sandbox no manda plantillas.","security":[{"bearerAuth":[]}],"x-heysteff-scopes":["messages:write"],"parameters":[{"schema":{"type":"string","format":"uuid"},"required":false,"name":"channel_connection_id","in":"query"},{"schema":{"type":"string","enum":["APPROVED","PENDING","REJECTED","PAUSED","DISABLED","all"],"default":"APPROVED"},"required":false,"name":"status","in":"query"}],"responses":{"200":{"description":"Plantillas.","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/Template"}}},"required":["data"]}}}},"400":{"description":"El cuerpo o los parámetros no pasan la validación (`validation_error`, con el detalle por campo en `details`) o el JSON es inválido (`invalid_json`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"Falta la API key, es inválida, venció o fue revocada (`unauthorized`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"La API key no tiene el scope requerido, la IP no está permitida o el workspace está deshabilitado.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"429":{"description":"Se superó el límite de solicitudes de la API key (`rate_limited`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}},"post":{"operationId":"createTemplate","tags":["Mensajes"],"summary":"Crear una plantilla de WhatsApp","description":"Crea una plantilla de WhatsApp Cloud y la manda a revisión de Meta. Responde con la plantilla, casi siempre en `PENDING`; el estado cambia solo cuando Meta la aprueba o rechaza (míralo con GET /v1/templates?status=all). Sin `channel_connection_id` usa el número de WhatsApp Cloud por defecto de la API. El nombre va en minúsculas con guiones bajos; cada variable `{{n}}` del cuerpo necesita su ejemplo en `body_examples`. Los encabezados con imagen, video o documento se crean desde el panel. Hasta 20 por hora.","security":[{"bearerAuth":[]}],"x-heysteff-scopes":["templates:write"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"channel_connection_id":{"type":"string","format":"uuid"},"name":{"type":"string","minLength":1,"maxLength":512,"pattern":"^[a-z0-9_]+$"},"language":{"type":"string","minLength":2,"maxLength":10},"category":{"type":"string","enum":["MARKETING","UTILITY","AUTHENTICATION"]},"header_text":{"type":"string","maxLength":60},"header_example":{"type":"string","maxLength":60},"header_media_format":{"type":"string","enum":["IMAGE","VIDEO","GIF","DOCUMENT"]},"header_media_handle":{"type":"string","minLength":1,"maxLength":2000},"body_text":{"type":"string","minLength":1,"maxLength":1024},"body_examples":{"type":"array","items":{"type":"string","maxLength":200},"maxItems":10},"footer_text":{"type":"string","maxLength":60},"buttons":{"type":"array","items":{"oneOf":[{"type":"object","properties":{"type":{"type":"string","enum":["QUICK_REPLY"]},"text":{"type":"string","minLength":1,"maxLength":25}},"required":["type","text"]},{"type":"object","properties":{"type":{"type":"string","enum":["URL"]},"text":{"type":"string","minLength":1,"maxLength":25},"url":{"type":"string","format":"uri"},"url_example":{"type":"string","minLength":1,"maxLength":2000}},"required":["type","text","url"]},{"type":"object","properties":{"type":{"type":"string","enum":["PHONE_NUMBER"]},"text":{"type":"string","minLength":1,"maxLength":25},"phone_number":{"type":"string","pattern":"^\\+?[1-9]\\d{1,14}$"}},"required":["type","text","phone_number"]},{"type":"object","properties":{"type":{"type":"string","enum":["FLOW"]},"text":{"type":"string","minLength":1,"maxLength":25},"flow_id":{"type":"string","pattern":"^\\d+$"},"flow_action":{"type":"string","enum":["navigate","data_exchange"]},"navigate_screen":{"type":"string","minLength":1,"maxLength":100}},"required":["type","text","flow_id"]},{"type":"object","properties":{"type":{"type":"string","enum":["VOICE_CALL"]},"text":{"type":"string","minLength":1,"maxLength":20},"ttl_minutes":{"type":"integer","minimum":1440,"maximum":43200}},"required":["type","text"]}]},"maxItems":3},"marketing":{"type":"object","properties":{"ttl_seconds":{"type":["integer","null"],"minimum":43200,"maximum":2592000},"creative_features":{"type":"object","properties":{"image_brightness_and_contrast":{"type":"string","enum":["OPT_IN","OPT_OUT"]},"image_touchups":{"type":"string","enum":["OPT_IN","OPT_OUT"]},"add_text_overlay":{"type":"string","enum":["OPT_IN","OPT_OUT"]},"image_animation":{"type":"string","enum":["OPT_IN","OPT_OUT"]},"image_background_gen":{"type":"string","enum":["OPT_IN","OPT_OUT"]},"auto_promotion_tag":{"type":"string","enum":["OPT_IN","OPT_OUT"]},"text_extraction_for_headline":{"type":"string","enum":["OPT_IN","OPT_OUT"]},"text_extraction_for_tap_target":{"type":"string","enum":["OPT_IN","OPT_OUT"]},"product_extensions":{"type":"string","enum":["OPT_IN","OPT_OUT"]},"text_formatting_optimization":{"type":"string","enum":["OPT_IN","OPT_OUT"]}}},"link_tracking_opted_out":{"type":"boolean"},"max_price":{"type":["object","null"],"properties":{"bid_amount":{"type":"integer","exclusiveMinimum":0},"bid_strategy":{"type":"string","enum":["LOWEST_COST_WITH_BID_CAP"],"default":"LOWEST_COST_WITH_BID_CAP"},"bid_country_multiplier_overrides":{"type":"object","additionalProperties":{"type":"number","exclusiveMinimum":0,"maximum":10}}},"required":["bid_amount"]}}}},"required":["name","language","category","body_text"]}}}},"responses":{"201":{"description":"Plantilla enviada a revisión.","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/Template"}},"required":["data"]}}}},"400":{"description":"El cuerpo o los parámetros no pasan la validación (`validation_error`, con el detalle por campo en `details`) o el JSON es inválido (`invalid_json`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"Falta la API key, es inválida, venció o fue revocada (`unauthorized`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"La API key no tiene el scope requerido, la IP no está permitida o el workspace está deshabilitado.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"El recurso no existe en este workspace (`not_found`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"422":{"description":"No hay un número de WhatsApp Cloud (`no_channel_connection`), faltan ejemplos de variables (`missing_examples`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"424":{"description":"Meta rechazó la plantilla al recibirla (`remote_error`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"429":{"description":"Se superó el límite de solicitudes de la API key (`rate_limited`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/v1/templates/{name}":{"delete":{"operationId":"deleteTemplate","tags":["Mensajes"],"summary":"Borrar una plantilla de WhatsApp","description":"Borra una plantilla en Meta y en heysteff. Sin `language` borra todas sus traducciones; con `channel_connection_id`, solo la de ese número. Meta no deja reusar el nombre de una plantilla borrada por 30 días.","security":[{"bearerAuth":[]}],"x-heysteff-scopes":["templates:write"],"parameters":[{"schema":{"type":"string","example":"pedido_despachado"},"required":true,"name":"name","in":"path"},{"schema":{"type":"string","minLength":2,"maxLength":16},"required":false,"name":"language","in":"query"},{"schema":{"type":"string","format":"uuid"},"required":false,"name":"channel_connection_id","in":"query"}],"responses":{"200":{"description":"Borrada.","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"name":{"type":"string"},"deleted":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"language":{"type":"string"},"channel_connection_id":{"type":"string","format":"uuid"}},"required":["id","language","channel_connection_id"]}}},"required":["name","deleted"]}},"required":["data"]}}}},"400":{"description":"El cuerpo o los parámetros no pasan la validación (`validation_error`, con el detalle por campo en `details`) o el JSON es inválido (`invalid_json`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"Falta la API key, es inválida, venció o fue revocada (`unauthorized`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"La API key no tiene el scope requerido, la IP no está permitida o el workspace está deshabilitado.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"El recurso no existe en este workspace (`not_found`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"424":{"description":"Meta rechazó el borrado (`remote_error`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"429":{"description":"Se superó el límite de solicitudes de la API key (`rate_limited`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/v1/conversations":{"get":{"operationId":"listConversations","tags":["Conversaciones"],"summary":"Listar conversaciones","description":"Conversaciones del workspace, de la más reciente a la más antigua, paginadas por cursor.","security":[{"bearerAuth":[]}],"x-heysteff-scopes":["conversations:read"],"parameters":[{"schema":{"type":"string","minLength":1,"maxLength":256},"required":false,"name":"cursor","in":"query"},{"schema":{"type":"integer","minimum":1,"maximum":200},"required":false,"name":"limit","in":"query"},{"schema":{"type":"string","enum":["open","closed","snoozed"]},"required":false,"name":"status","in":"query"},{"schema":{"type":"string","enum":["instagram","facebook","whatsapp_cloud","whatsapp_baileys","gmail","sms","shopify","mercadolibre"]},"required":false,"name":"channel","in":"query"},{"schema":{"type":"string","format":"uuid"},"required":false,"name":"customer_id","in":"query"}],"responses":{"200":{"description":"Página de conversaciones.","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/Conversation"}},"cursor":{"type":"object","properties":{"next":{"type":["string","null"]},"has_more":{"type":"boolean"}},"required":["next","has_more"]}},"required":["data","cursor"]}}}},"400":{"description":"El cuerpo o los parámetros no pasan la validación (`validation_error`, con el detalle por campo en `details`) o el JSON es inválido (`invalid_json`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"Falta la API key, es inválida, venció o fue revocada (`unauthorized`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"La API key no tiene el scope requerido, la IP no está permitida o el workspace está deshabilitado.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"429":{"description":"Se superó el límite de solicitudes de la API key (`rate_limited`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/v1/conversations/{id}/messages":{"get":{"operationId":"listConversationMessages","tags":["Conversaciones"],"summary":"Listar mensajes de una conversación","description":"Mensajes de una conversación, paginados por cursor.","security":[{"bearerAuth":[]}],"x-heysteff-scopes":["conversations:read"],"parameters":[{"schema":{"type":"string","format":"uuid","example":"6f1c2a8e-3b4d-4e5f-9a0b-1c2d3e4f5a6b"},"required":true,"name":"id","in":"path"},{"schema":{"type":"string","minLength":1,"maxLength":256},"required":false,"name":"cursor","in":"query"},{"schema":{"type":"integer","minimum":1,"maximum":200},"required":false,"name":"limit","in":"query"}],"responses":{"200":{"description":"Página de mensajes.","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/ConversationMessage"}},"cursor":{"type":"object","properties":{"next":{"type":["string","null"]},"has_more":{"type":"boolean"}},"required":["next","has_more"]}},"required":["data","cursor"]}}}},"400":{"description":"El cuerpo o los parámetros no pasan la validación (`validation_error`, con el detalle por campo en `details`) o el JSON es inválido (`invalid_json`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"Falta la API key, es inválida, venció o fue revocada (`unauthorized`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"La API key no tiene el scope requerido, la IP no está permitida o el workspace está deshabilitado.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"429":{"description":"Se superó el límite de solicitudes de la API key (`rate_limited`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/v1/conversations/{id}":{"get":{"operationId":"getConversation","tags":["Conversaciones"],"summary":"Leer una conversación","description":"Una conversación con lo necesario para contestarle (`channel`, `external_user_id` y `channel_connection_id` van tal cual a POST /v1/messages) y su estado: si el bot está pausado y por quién, la escalación abierta y la asignación. Con una key de prueba solo existen las conversaciones del sandbox.","security":[{"bearerAuth":[]}],"x-heysteff-scopes":["conversations:read"],"parameters":[{"schema":{"type":"string","format":"uuid","example":"6f1c2a8e-3b4d-4e5f-9a0b-1c2d3e4f5a6b"},"required":true,"name":"id","in":"path"}],"responses":{"200":{"description":"La conversación.","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/ConversationDetail"}},"required":["data"]}}}},"400":{"description":"El id no es un UUID (`invalid_id`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"Falta la API key, es inválida, venció o fue revocada (`unauthorized`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"La API key no tiene el scope requerido, la IP no está permitida o el workspace está deshabilitado.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"El recurso no existe en este workspace (`not_found`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"429":{"description":"Se superó el límite de solicitudes de la API key (`rate_limited`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/v1/conversations/{id}/escalate":{"post":{"operationId":"escalateConversation","tags":["Conversaciones"],"summary":"Pasar a una persona","description":"Tu IA no sabe qué responder: pasa la conversación a una persona del equipo en la bandeja de heysteff. Abre una escalación, pausa el bot (`bot_paused_by` = `api_escalation`), la reabre si estaba archivada, avisa al equipo y calcula un resumen con su urgencia. Idempotente: si ya hay una escalación abierta no abre otra (`already_open: true`, status 200), pero el `reason` nuevo se suma al caso. En un espacio personal no hay equipo: responde 422 `personal_workspace_no_escalation`.","security":[{"bearerAuth":[]}],"x-heysteff-scopes":["conversations:write"],"parameters":[{"schema":{"type":"string","format":"uuid","example":"6f1c2a8e-3b4d-4e5f-9a0b-1c2d3e4f5a6b"},"required":true,"name":"id","in":"path"}],"requestBody":{"required":false,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/EscalateConversationRequest"}}}},"responses":{"200":{"description":"Ya había una escalación abierta.","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"escalation_id":{"type":["string","null"],"format":"uuid"},"already_open":{"type":"boolean"},"conversation":{"$ref":"#/components/schemas/ConversationDetail"}},"required":["escalation_id","already_open","conversation"]}},"required":["data"]}}}},"201":{"description":"Escalación abierta.","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"escalation_id":{"type":["string","null"],"format":"uuid"},"already_open":{"type":"boolean"},"conversation":{"$ref":"#/components/schemas/ConversationDetail"}},"required":["escalation_id","already_open","conversation"]}},"required":["data"]}}}},"400":{"description":"El cuerpo o los parámetros no pasan la validación (`validation_error`, con el detalle por campo en `details`) o el JSON es inválido (`invalid_json`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"Falta la API key, es inválida, venció o fue revocada (`unauthorized`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"La API key no tiene el scope requerido, la IP no está permitida o el workspace está deshabilitado.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"El recurso no existe en este workspace (`not_found`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"422":{"description":"Espacio personal (`personal_workspace_no_escalation`): pausa el bot en vez de escalar.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"429":{"description":"Se superó el límite de solicitudes de la API key (`rate_limited`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/v1/conversations/{id}/bot":{"post":{"operationId":"setConversationBot","tags":["Conversaciones"],"summary":"Pausar o reanudar el bot","description":"`{\"paused\": true}` pausa el bot sin fecha de vuelta (`bot_paused_by` = `api`), corta el flujo que llevaba la conversación y no pisa una pausa que ya existía. `{\"paused\": false}` lo reanuda y cierra la escalación abierta, como \"Reactivar bot\" en la bandeja; la pausa de un flujo que sigue corriendo no se levanta (`kept_flow_pause: true`).","security":[{"bearerAuth":[]}],"x-heysteff-scopes":["conversations:write"],"parameters":[{"schema":{"type":"string","format":"uuid","example":"6f1c2a8e-3b4d-4e5f-9a0b-1c2d3e4f5a6b"},"required":true,"name":"id","in":"path"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ConversationBotRequest"}}}},"responses":{"200":{"description":"Estado del bot.","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"changed":{"type":"boolean","description":"false = ya estaba así (o quedó la pausa de un flujo vivo)."},"kept_flow_pause":{"type":"boolean","description":"Al reanudar: un flujo sigue corriendo y su pausa se respeta."},"resolved_escalations":{"type":"integer","description":"Al reanudar: escalaciones abiertas que se cerraron."},"conversation":{"$ref":"#/components/schemas/ConversationDetail"}},"required":["changed","kept_flow_pause","resolved_escalations","conversation"]}},"required":["data"]}}}},"400":{"description":"El cuerpo o los parámetros no pasan la validación (`validation_error`, con el detalle por campo en `details`) o el JSON es inválido (`invalid_json`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"Falta la API key, es inválida, venció o fue revocada (`unauthorized`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"La API key no tiene el scope requerido, la IP no está permitida o el workspace está deshabilitado.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"El recurso no existe en este workspace (`not_found`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"429":{"description":"Se superó el límite de solicitudes de la API key (`rate_limited`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/v1/conversations/{id}/resolve":{"post":{"operationId":"resolveConversation","tags":["Conversaciones"],"summary":"Resolver una conversación","description":"El \"Resolver\" de la bandeja: cierra las escalaciones abiertas, reactiva el bot, archiva la conversación y la mueve a la columna de resolución del embudo si hay una. Idempotente (`changed: false` si ya estaba resuelta). El próximo mensaje del cliente la vuelve a abrir.","security":[{"bearerAuth":[]}],"x-heysteff-scopes":["conversations:write"],"parameters":[{"schema":{"type":"string","format":"uuid","example":"6f1c2a8e-3b4d-4e5f-9a0b-1c2d3e4f5a6b"},"required":true,"name":"id","in":"path"}],"responses":{"200":{"description":"Conversación resuelta.","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"changed":{"type":"boolean"},"resolved_escalations":{"type":"integer"},"moved_to_column_id":{"type":["string","null"],"format":"uuid"},"conversation":{"$ref":"#/components/schemas/ConversationDetail"}},"required":["changed","resolved_escalations","moved_to_column_id","conversation"]}},"required":["data"]}}}},"400":{"description":"El id no es un UUID (`invalid_id`), o la columna de resolución exige datos del ticket que faltan (`missing_required_fields`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"Falta la API key, es inválida, venció o fue revocada (`unauthorized`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"La API key no tiene el scope requerido, la IP no está permitida o el workspace está deshabilitado.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"El recurso no existe en este workspace (`not_found`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"429":{"description":"Se superó el límite de solicitudes de la API key (`rate_limited`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/v1/contacts":{"get":{"operationId":"listContacts","tags":["Contactos"],"summary":"Listar contactos","description":"Contactos del workspace, paginados por cursor. `q` busca por nombre, correo o teléfono.","security":[{"bearerAuth":[]}],"x-heysteff-scopes":["contacts:read"],"parameters":[{"schema":{"type":"string","minLength":1,"maxLength":256},"required":false,"name":"cursor","in":"query"},{"schema":{"type":"integer","minimum":1,"maximum":200},"required":false,"name":"limit","in":"query"},{"schema":{"type":"string","minLength":1,"maxLength":256},"required":false,"name":"q","in":"query"}],"responses":{"200":{"description":"Página de contactos.","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/Contact"}},"cursor":{"type":"object","properties":{"next":{"type":["string","null"]},"has_more":{"type":"boolean"}},"required":["next","has_more"]}},"required":["data","cursor"]}}}},"400":{"description":"El cuerpo o los parámetros no pasan la validación (`validation_error`, con el detalle por campo en `details`) o el JSON es inválido (`invalid_json`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"Falta la API key, es inválida, venció o fue revocada (`unauthorized`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"La API key no tiene el scope requerido, la IP no está permitida o el workspace está deshabilitado.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"429":{"description":"Se superó el límite de solicitudes de la API key (`rate_limited`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}},"post":{"operationId":"createContact","tags":["Contactos"],"summary":"Crear un contacto","description":"Crea un contacto. Necesita al menos un dato que lo identifique (teléfono, correo o nombre). El teléfono se normaliza a E.164.","security":[{"bearerAuth":[]}],"x-heysteff-scopes":["contacts:write"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string","minLength":1,"maxLength":200},"email":{"type":"string","maxLength":256,"format":"email"},"phone":{"type":"string","minLength":1,"maxLength":50},"phone_country":{"type":"string","pattern":"^[A-Za-z]{2}$"},"avatar_url":{"type":"string","maxLength":2048,"format":"uri"},"external_ids":{"type":"object","additionalProperties":{"type":"string","maxLength":256}},"tags":{"type":"array","items":{"type":"string","minLength":1,"maxLength":50},"maxItems":50},"metadata":{"type":"object","additionalProperties":{}},"birth_month":{"type":"integer","minimum":1,"maximum":12},"birth_day":{"type":"integer","minimum":1,"maximum":31},"birth_year":{"type":"integer","minimum":1900,"maximum":2100}}}}}},"responses":{"201":{"description":"Contacto creado.","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/Contact"}},"required":["data"]}}}},"400":{"description":"El cuerpo o los parámetros no pasan la validación (`validation_error`, con el detalle por campo en `details`) o el JSON es inválido (`invalid_json`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"Falta la API key, es inválida, venció o fue revocada (`unauthorized`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"La API key no tiene el scope requerido, la IP no está permitida o el workspace está deshabilitado.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"409":{"description":"Ya existe un contacto con ese teléfono (`duplicate_phone`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"422":{"description":"La solicitud es válida pero no se puede procesar (por ejemplo, no hay un canal conectado para enviar).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"429":{"description":"Se superó el límite de solicitudes de la API key (`rate_limited`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/v1/contact-lists":{"get":{"operationId":"listContactLists","tags":["Listas de contactos"],"summary":"Listar listas de contactos","security":[{"bearerAuth":[]}],"x-heysteff-scopes":["contacts:read"],"responses":{"200":{"description":"Listas del workspace.","content":{"application/json":{"schema":{"type":"object","properties":{"lists":{"type":"array","items":{"$ref":"#/components/schemas/ContactList"}}},"required":["lists"]}}}},"401":{"description":"Falta la API key, es inválida, venció o fue revocada (`unauthorized`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"La API key no tiene el scope requerido, la IP no está permitida o el workspace está deshabilitado.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"429":{"description":"Se superó el límite de solicitudes de la API key (`rate_limited`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/v1/contact-lists/{id}/members":{"post":{"operationId":"addContactListMembers","tags":["Listas de contactos"],"summary":"Agregar contactos a una lista","description":"Agrega contactos a una lista sin pasar por una planilla. Envía exactamente uno de `rows` (filas crudas, con el mismo mapeo de columnas del import) o `members` (contactos estructurados). Con `preview_only: true` devuelve el resumen sin guardar. Si una campaña activa usa la lista, los contactos nuevos reciben su mensaje.","security":[{"bearerAuth":[]}],"x-heysteff-scopes":["contacts:write"],"parameters":[{"schema":{"type":"string","format":"uuid","example":"6f1c2a8e-3b4d-4e5f-9a0b-1c2d3e4f5a6b"},"required":true,"name":"id","in":"path"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"rows":{"type":"array","items":{"type":"object","additionalProperties":{"type":"string"}},"maxItems":5000},"members":{"type":"array","items":{"type":"object","properties":{"phone":{"type":"string","minLength":3,"maxLength":32},"first_name":{"type":"string","maxLength":200},"last_name":{"type":"string","maxLength":200},"email":{"type":"string","maxLength":320},"variables":{"type":"object","additionalProperties":{"type":"string","maxLength":1000}}},"required":["phone"]},"maxItems":5000},"column_mapping":{"type":"object","additionalProperties":{"type":"string"}},"preview_only":{"type":"boolean","default":false}}}}}},"responses":{"200":{"description":"Resumen del import: filas válidas, inválidas, duplicadas y omitidas por opt-out.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ContactListImportSummary"}}}},"400":{"description":"El cuerpo o los parámetros no pasan la validación (`validation_error`, con el detalle por campo en `details`) o el JSON es inválido (`invalid_json`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"Falta la API key, es inválida, venció o fue revocada (`unauthorized`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"402":{"description":"Se alcanzó un límite del plan (`plan_limit_exceeded`, con `upgrade_url` en `details`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"La API key no tiene el scope requerido, la IP no está permitida o el workspace está deshabilitado.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"El recurso no existe en este workspace (`not_found`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"422":{"description":"La solicitud es válida pero no se puede procesar (por ejemplo, no hay un canal conectado para enviar).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"429":{"description":"Se superó el límite de solicitudes de la API key (`rate_limited`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/v1/campaigns":{"post":{"operationId":"createCampaign","tags":["Campañas"],"summary":"Crear una campaña","description":"Crea una campaña. Con `trigger_type: \"api\"` y `trigger: true` la corrida se dispara en la misma solicitud.","security":[{"bearerAuth":[]}],"x-heysteff-scopes":["campaigns:write"],"parameters":[{"schema":{"type":"string","minLength":1,"maxLength":256,"description":"Clave única por operación lógica. Si repites la solicitud con la misma clave, recibes exactamente la misma respuesta que la primera vez, sin efectos duplicados.","example":"pedido-1042-confirmacion"},"required":false,"name":"Idempotency-Key","in":"header"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string","minLength":1,"maxLength":256},"channel":{"type":"string","enum":["instagram","facebook","whatsapp_cloud","whatsapp_baileys","gmail","sms","shopify"]},"trigger_type":{"type":"string","enum":["scheduled","event","api"]},"message_mode":{"type":"string","enum":["template","free_text","ai_generated","email_template","saved_message"]},"trigger_config":{"type":"object","additionalProperties":{}},"audience_source":{"type":"object","properties":{"kind":{"type":"string","enum":["contact_list","custom_tool","internal_query","event"]},"ref":{"type":"object","additionalProperties":{}}},"required":["kind"]},"message_config":{"type":"object","additionalProperties":{}},"suppression_config":{"type":"object","additionalProperties":{}},"trigger":{"type":"boolean"}},"required":["name","channel","trigger_type","message_mode","audience_source","message_config"]}}}},"responses":{"201":{"description":"Campaña creada.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CampaignCreated"}}}},"400":{"description":"El cuerpo o los parámetros no pasan la validación (`validation_error`, con el detalle por campo en `details`) o el JSON es inválido (`invalid_json`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"Falta la API key, es inválida, venció o fue revocada (`unauthorized`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"402":{"description":"Se alcanzó un límite del plan (`plan_limit_exceeded`, con `upgrade_url` en `details`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"La API key no tiene el scope requerido, la IP no está permitida o el workspace está deshabilitado.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"409":{"description":"Ya hay una solicitud con la misma `Idempotency-Key` en proceso (`idempotency_conflict`). Reintenta en unos segundos.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"422":{"description":"La solicitud es válida pero no se puede procesar (por ejemplo, no hay un canal conectado para enviar).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"429":{"description":"Se superó el límite de solicitudes de la API key (`rate_limited`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/v1/campaigns/{id}":{"get":{"operationId":"getCampaign","tags":["Campañas"],"summary":"Obtener una campaña","description":"Estado de la campaña y conteo de destinatarios por estado.","security":[{"bearerAuth":[]}],"x-heysteff-scopes":["campaigns:read"],"parameters":[{"schema":{"type":"string","format":"uuid","example":"6f1c2a8e-3b4d-4e5f-9a0b-1c2d3e4f5a6b"},"required":true,"name":"id","in":"path"}],"responses":{"200":{"description":"Campaña.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Campaign"}}}},"400":{"description":"El cuerpo o los parámetros no pasan la validación (`validation_error`, con el detalle por campo en `details`) o el JSON es inválido (`invalid_json`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"Falta la API key, es inválida, venció o fue revocada (`unauthorized`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"La API key no tiene el scope requerido, la IP no está permitida o el workspace está deshabilitado.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"El recurso no existe en este workspace (`not_found`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"429":{"description":"Se superó el límite de solicitudes de la API key (`rate_limited`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/v1/events":{"get":{"operationId":"listEvents","tags":["Eventos"],"summary":"Leer el stream de eventos","description":"Stream ordenado de eventos del workspace, para consumirlo por polling como complemento de los webhooks. Guarda el `next_after_seq` y pásalo en la siguiente llamada.","security":[{"bearerAuth":[]}],"x-heysteff-scopes":["events:read"],"parameters":[{"schema":{"type":"string","pattern":"^\\d+$","description":"Devuelve los eventos con `seq` mayor a este. Usa el `next_after_seq` de la página anterior."},"required":false,"name":"after_seq","in":"query"},{"schema":{"type":"integer","minimum":1,"maximum":200,"description":"Cantidad por página (1 a 200, 50 por defecto)."},"required":false,"name":"limit","in":"query"}],"responses":{"200":{"description":"Página de eventos.","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/DomainEvent"}},"cursor":{"type":"object","properties":{"next_after_seq":{"type":["string","null"]},"has_more":{"type":"boolean"}},"required":["next_after_seq","has_more"]}},"required":["data","cursor"]}}}},"400":{"description":"`after_seq` no es un entero positivo (`invalid_cursor`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"Falta la API key, es inválida, venció o fue revocada (`unauthorized`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"La API key no tiene el scope requerido, la IP no está permitida o el workspace está deshabilitado.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"429":{"description":"Se superó el límite de solicitudes de la API key (`rate_limited`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}},"post":{"operationId":"createEvent","tags":["Eventos"],"summary":"Registrar un evento externo","description":"Publica un evento de tu sistema (por ejemplo, de tu ecommerce) en el stream del workspace, donde lo pueden usar campañas y flujos. `type` debe estar en la lista de eventos externos permitidos. `idempotency_key` deduplica: repetir la misma clave devuelve la respuesta original.","security":[{"bearerAuth":[]}],"x-heysteff-scopes":["events:write"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"type":{"type":"string","minLength":1,"maxLength":128},"contact_ref":{"type":"string","minLength":1,"maxLength":256},"payload":{"type":"object","additionalProperties":{},"default":{}},"occurred_at":{"type":"string","format":"date-time"},"idempotency_key":{"type":"string","minLength":1,"maxLength":256}},"required":["type","contact_ref","idempotency_key"]}}}},"responses":{"201":{"description":"Evento registrado.","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"seq":{"type":"string"},"occurred_at":{"type":"string","format":"date-time"}},"required":["id","seq","occurred_at"]}},"required":["data"]}}}},"400":{"description":"El cuerpo o los parámetros no pasan la validación (`validation_error`, con el detalle por campo en `details`) o el JSON es inválido (`invalid_json`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"Falta la API key, es inválida, venció o fue revocada (`unauthorized`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"La API key no tiene el scope requerido, la IP no está permitida o el workspace está deshabilitado.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"409":{"description":"Ya hay una solicitud con la misma `Idempotency-Key` en proceso (`idempotency_conflict`). Reintenta en unos segundos.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"422":{"description":"El `type` no está en la lista de eventos externos permitidos (`event_type_not_allowed`, con la lista en `details.allowed_types`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"429":{"description":"Se superó el límite de solicitudes de la API key (`rate_limited`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/v1/events/stream":{"get":{"operationId":"streamEvents","tags":["Eventos"],"summary":"Recibir los eventos en vivo (Server-Sent Events)","description":"Los mismos eventos y el mismo sobre que los webhooks (`{ id, tenant_id, event, ts, data }`), en vivo por Server-Sent Events: lo que usa `heysteff listen`. Al abrir llega `retry: 3000` y un `event: ready` con `{ tenant_id, mode }`; después, por evento, `id`, `event` (el nombre) y `data` (el sobre en JSON). Un comentario `: ping` cada 25 s. El servidor corta a la hora: reconecta con la cabecera `Last-Event-ID` y recibes lo que te perdiste (se guardan hasta 1.000 eventos durante 10 minutos mientras alguien escucha). Una key `sk_test_` entra con `conversations:read` y solo recibe los eventos de las conversaciones del sandbox.","security":[{"bearerAuth":[]}],"x-heysteff-scopes":["events:read"],"parameters":[{"schema":{"type":"string","description":"Nombres de eventos separados por coma (los mismos de los webhooks). Sin filtro llegan todos.","example":"message:received,message.read"},"required":false,"name":"events","in":"query"},{"schema":{"type":"string","description":"El `id` del último evento que recibiste, para recuperar lo que pasó mientras estabas desconectado.","example":"1731600000000-0"},"required":false,"name":"Last-Event-ID","in":"header"}],"responses":{"200":{"description":"Stream de eventos (`text/event-stream`).","content":{"text/event-stream":{"schema":{"type":"string","example":"id: 1731600000000-0\nevent: message:received\ndata: {\"id\":\"…\",\"tenant_id\":\"…\",\"event\":\"message:received\",\"ts\":\"…\",\"data\":{…}}\n\n"}}}},"401":{"description":"Falta la API key, es inválida, venció o fue revocada (`unauthorized`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"La API key no tiene el scope requerido, la IP no está permitida o el workspace está deshabilitado.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"422":{"description":"Algún nombre de `events` no existe (`invalid_event`, con la lista en `details.unknown`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"429":{"description":"Se superó el límite de solicitudes de la API key (`rate_limited`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/v1/conversions":{"post":{"operationId":"createConversion","tags":["Conversiones"],"summary":"Reportar una conversión","description":"Reporta una acción de valor (compra, reserva, registro) para atribuirla a la campaña que la generó. `value` va en unidades menores (centavos), entero. `external_ref` es el id de la orden en tu sistema y deduplica: repetirlo devuelve la respuesta original.","security":[{"bearerAuth":[]}],"x-heysteff-scopes":["conversions:write"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"contact_ref":{"type":"string","minLength":1,"maxLength":256},"type":{"type":"string","enum":["purchase","booking","signup","lead","custom"]},"value":{"type":"integer","minimum":0},"currency":{"type":"string","minLength":1,"maxLength":8},"occurred_at":{"type":"string","format":"date-time"},"external_ref":{"type":"string","minLength":1,"maxLength":256}},"required":["contact_ref","type","external_ref"]}}}},"responses":{"201":{"description":"Conversión registrada, con el resultado de la atribución.","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"attributed":{"type":"boolean"},"attributed_campaign_id":{"type":["string","null"],"format":"uuid"},"attributed_recipient_id":{"type":["string","null"],"format":"uuid"}},"required":["id","attributed","attributed_campaign_id","attributed_recipient_id"]}},"required":["data"]}}}},"400":{"description":"El cuerpo o los parámetros no pasan la validación (`validation_error`, con el detalle por campo en `details`) o el JSON es inválido (`invalid_json`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"Falta la API key, es inválida, venció o fue revocada (`unauthorized`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"La API key no tiene el scope requerido, la IP no está permitida o el workspace está deshabilitado.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"409":{"description":"Ya hay una solicitud con la misma `Idempotency-Key` en proceso (`idempotency_conflict`). Reintenta en unos segundos.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"429":{"description":"Se superó el límite de solicitudes de la API key (`rate_limited`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/v1/reviews":{"get":{"operationId":"listReviews","tags":["Reseñas"],"summary":"Listar reseñas","description":"Reseñas de productos del workspace, de la más reciente a la más antigua, paginadas por `limit` y `offset`.","security":[{"bearerAuth":[]}],"x-heysteff-scopes":["reviews:read"],"parameters":[{"schema":{"type":"string","enum":["pending","approved","rejected","archived"]},"required":false,"name":"status","in":"query"},{"schema":{"type":"string","maxLength":256},"required":false,"name":"product_id","in":"query"},{"schema":{"type":"integer","minimum":1,"maximum":5},"required":false,"name":"min_rating","in":"query"},{"schema":{"type":"integer","minimum":1,"maximum":200},"required":false,"name":"limit","in":"query"},{"schema":{"type":["integer","null"],"minimum":0},"required":false,"name":"offset","in":"query"}],"responses":{"200":{"description":"Página de reseñas.","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/Review"}},"pagination":{"type":"object","properties":{"limit":{"type":"integer"},"offset":{"type":"integer"},"returned":{"type":"integer"}},"required":["limit","offset","returned"]}},"required":["data","pagination"]}}}},"400":{"description":"El cuerpo o los parámetros no pasan la validación (`validation_error`, con el detalle por campo en `details`) o el JSON es inválido (`invalid_json`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"Falta la API key, es inválida, venció o fue revocada (`unauthorized`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"La API key no tiene el scope requerido, la IP no está permitida o el workspace está deshabilitado.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"429":{"description":"Se superó el límite de solicitudes de la API key (`rate_limited`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}},"post":{"operationId":"createReviewRequest","tags":["Reseñas"],"summary":"Pedir una reseña","description":"Le manda a un contacto la solicitud de reseña de un pedido y deja abierta la captura de su respuesta. `external_ref` es la referencia del pedido en tu sistema: si ya hay una solicitud abierta o una reseña guardada para ese pedido, responde 200 sin reenviar.","security":[{"bearerAuth":[]}],"x-heysteff-scopes":["reviews:write"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"contact_ref":{"type":"string","minLength":1},"external_ref":{"type":"string","minLength":1},"contact_name":{"type":"string"},"product":{"type":"string"},"channel":{"type":"string"}},"required":["contact_ref","external_ref"]}}}},"responses":{"200":{"description":"No se reenvió: ya había una solicitud abierta (`already_open`) o una reseña guardada (`already_reviewed`).","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"conversation_id":{"type":"string","format":"uuid"},"external_ref":{"type":"string"},"already_open":{"type":"boolean"},"already_reviewed":{"type":"boolean"}},"required":["external_ref"]}},"required":["data"]}}}},"201":{"description":"Solicitud enviada.","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"conversation_id":{"type":"string","format":"uuid"},"message_id":{"type":"string","format":"uuid"},"external_ref":{"type":"string"}},"required":["conversation_id","message_id","external_ref"]}},"required":["data"]}}}},"400":{"description":"El cuerpo o los parámetros no pasan la validación (`validation_error`, con el detalle por campo en `details`) o el JSON es inválido (`invalid_json`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"Falta la API key, es inválida, venció o fue revocada (`unauthorized`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"402":{"description":"Se alcanzó un límite del plan (`plan_limit_exceeded`, con `upgrade_url` en `details`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"La API key no tiene el scope requerido, la IP no está permitida o el workspace está deshabilitado.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"422":{"description":"La solicitud es válida pero no se puede procesar (por ejemplo, no hay un canal conectado para enviar).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"429":{"description":"Se superó el límite de solicitudes de la API key (`rate_limited`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/v1/charges":{"post":{"operationId":"createCharge","tags":["Cobros"],"summary":"Cobrarle a un contacto","description":"Genera un link de pago con el proveedor de cobros del workspace y se lo manda al contacto por el canal indicado. `amount` va en unidades menores (centavos), entero positivo. El header `Idempotency-Key` es obligatorio. Fuera de la ventana de 24 h de WhatsApp Cloud usa `template_name`.","security":[{"bearerAuth":[]}],"x-heysteff-scopes":["charges:write"],"parameters":[{"schema":{"type":"string","minLength":1,"maxLength":256,"description":"Clave única por operación lógica. Si repites la solicitud con la misma clave, recibes exactamente la misma respuesta que la primera vez, sin efectos duplicados.","example":"pedido-1042-confirmacion"},"required":true,"name":"Idempotency-Key","in":"header"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"contact_ref":{"type":"string","minLength":1,"maxLength":256},"channel":{"type":"string","enum":["instagram","facebook","whatsapp_cloud","whatsapp_baileys","gmail","sms","shopify"]},"amount":{"type":"integer","exclusiveMinimum":0},"currency":{"type":"string","minLength":3,"maxLength":8},"description":{"type":"string","minLength":1,"maxLength":1000},"metadata":{"type":"object","additionalProperties":{}},"template_name":{"type":"string","minLength":1,"maxLength":512},"template_language":{"type":"string","minLength":2,"maxLength":16},"template_components":{"type":"array","items":{"type":"object","additionalProperties":{}}}},"required":["contact_ref","channel","amount","currency","description"]}}}},"responses":{"201":{"description":"Cobro creado y mensaje encolado.","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"sale_id":{"type":"string","format":"uuid"},"payment_link_id":{"type":"string","format":"uuid"},"payment_link_url":{"type":"string","format":"uri"},"pending_approval":{"type":"boolean"},"campaign_id":{"type":["string","null"],"format":"uuid"}},"required":["sale_id","payment_link_id","payment_link_url","pending_approval","campaign_id"]}},"required":["data"]}}}},"400":{"description":"El cuerpo o los parámetros no pasan la validación (`validation_error`, con el detalle por campo en `details`) o el JSON es inválido (`invalid_json`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"Falta la API key, es inválida, venció o fue revocada (`unauthorized`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"402":{"description":"Se alcanzó un límite del plan (`plan_limit_exceeded`, con `upgrade_url` en `details`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"La API key no tiene el scope requerido, la IP no está permitida o el workspace está deshabilitado.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"409":{"description":"Ya hay una solicitud con la misma `Idempotency-Key` en proceso (`idempotency_conflict`). Reintenta en unos segundos.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"422":{"description":"La solicitud es válida pero no se puede procesar (por ejemplo, no hay un canal conectado para enviar).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"429":{"description":"Se superó el límite de solicitudes de la API key (`rate_limited`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/v1/refunds":{"post":{"operationId":"createRefund","tags":["Cobros"],"summary":"Reembolsar un pago","description":"Reembolsa un link de pago pagado, total o parcial. Sin `amount` el reembolso es total (el saldo restante). Todo reembolso queda auditado y, por defecto, espera la aprobación de una persona. El header `Idempotency-Key` es obligatorio.","security":[{"bearerAuth":[]}],"x-heysteff-scopes":["refunds:write"],"parameters":[{"schema":{"type":"string","minLength":1,"maxLength":256,"description":"Clave única por operación lógica. Si repites la solicitud con la misma clave, recibes exactamente la misma respuesta que la primera vez, sin efectos duplicados.","example":"pedido-1042-confirmacion"},"required":true,"name":"Idempotency-Key","in":"header"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"payment_link_id":{"type":"string","format":"uuid"},"amount":{"type":"integer","exclusiveMinimum":0},"reason":{"type":"string","minLength":1,"maxLength":1000}},"required":["payment_link_id"]}}}},"responses":{"201":{"description":"Reembolso creado.","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"refund_id":{"type":"string","format":"uuid"},"status":{"type":"string"},"amount":{"type":"integer"},"currency":{"type":"string"},"pending_approval":{"type":"boolean"}},"required":["refund_id","status","amount","currency","pending_approval"]}},"required":["data"]}}}},"400":{"description":"El cuerpo o los parámetros no pasan la validación (`validation_error`, con el detalle por campo en `details`) o el JSON es inválido (`invalid_json`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"Falta la API key, es inválida, venció o fue revocada (`unauthorized`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"La API key no tiene el scope requerido, la IP no está permitida o el workspace está deshabilitado.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"El recurso no existe en este workspace (`not_found`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"409":{"description":"Ya hay una solicitud con la misma `Idempotency-Key` en proceso (`idempotency_conflict`). Reintenta en unos segundos.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"422":{"description":"La solicitud es válida pero no se puede procesar (por ejemplo, no hay un canal conectado para enviar).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"429":{"description":"Se superó el límite de solicitudes de la API key (`rate_limited`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/v1/sales/funnel":{"get":{"operationId":"getSalesFunnel","tags":["Cobros"],"summary":"Embudo de ventas","description":"Ventas por etapa, ingresos pagados y reembolsos por moneda en la ventana `[since, until)` sobre la fecha de creación de cada venta. Montos en unidades menores.","security":[{"bearerAuth":[]}],"x-heysteff-scopes":["sales:read"],"parameters":[{"schema":{"type":"string","format":"date-time"},"required":false,"name":"since","in":"query"},{"schema":{"type":"string","format":"date-time"},"required":false,"name":"until","in":"query"}],"responses":{"200":{"description":"Embudo de ventas.","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/SalesFunnel"}},"required":["data"]}}}},"400":{"description":"El cuerpo o los parámetros no pasan la validación (`validation_error`, con el detalle por campo en `details`) o el JSON es inválido (`invalid_json`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"Falta la API key, es inválida, venció o fue revocada (`unauthorized`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"La API key no tiene el scope requerido, la IP no está permitida o el workspace está deshabilitado.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"429":{"description":"Se superó el límite de solicitudes de la API key (`rate_limited`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/v1/automation-flows/{id}/trigger":{"post":{"operationId":"triggerAutomationFlow","tags":["Flujos"],"summary":"Disparar un flujo","description":"Arranca una corrida de un flujo activo para un teléfono. Los campos extra del cuerpo quedan como variables de la corrida (hasta 32 KB). La corrida se deduplica por `dedup_key`, el header `Idempotency-Key` o, si no mandas ninguno, por flujo + teléfono + `order_number`.","security":[{"bearerAuth":[]}],"x-heysteff-scopes":["automations:trigger"],"parameters":[{"schema":{"type":"string","format":"uuid","example":"6f1c2a8e-3b4d-4e5f-9a0b-1c2d3e4f5a6b"},"required":true,"name":"id","in":"path"},{"schema":{"type":"string","minLength":1,"maxLength":256,"description":"Clave única por operación lógica. Si repites la solicitud con la misma clave, recibes exactamente la misma respuesta que la primera vez, sin efectos duplicados.","example":"pedido-1042-confirmacion"},"required":false,"name":"Idempotency-Key","in":"header"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"phone":{"type":"string","minLength":3,"maxLength":32},"order_number":{"type":"string","maxLength":120},"dedup_key":{"type":"string","maxLength":200}},"required":["phone"],"additionalProperties":{}}}}},"responses":{"200":{"description":"Ya existía una corrida con la misma clave de deduplicación: no se creó otra.","content":{"application/json":{"schema":{"type":"object","properties":{"run":{"type":"null"},"deduped":{"type":"boolean","enum":[true]}},"required":["run","deduped"]}}}},"202":{"description":"Corrida creada.","content":{"application/json":{"schema":{"type":"object","properties":{"run":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"status":{"type":"string"}},"required":["id","status"]}},"required":["run"]}}}},"400":{"description":"El cuerpo o los parámetros no pasan la validación (`validation_error`, con el detalle por campo en `details`) o el JSON es inválido (`invalid_json`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"Falta la API key, es inválida, venció o fue revocada (`unauthorized`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"La API key no tiene el scope requerido, la IP no está permitida o el workspace está deshabilitado.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"El recurso no existe en este workspace (`not_found`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"409":{"description":"El flujo no está activo (`flow_not_active`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"429":{"description":"Se superó el límite de solicitudes de la API key (`rate_limited`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/v1/owner/sleep":{"get":{"operationId":"getOwnerSleep","tags":["Espacio personal"],"summary":"Estado del modo sueño","description":"Si el dueño de un espacio personal está durmiendo. Mientras duerme, lo que el agente iba a mandar queda como sugerencia.","security":[{"bearerAuth":[]}],"x-heysteff-scopes":["owner:write"],"responses":{"200":{"description":"Estado actual.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OwnerSleep"}}}},"401":{"description":"Falta la API key, es inválida, venció o fue revocada (`unauthorized`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"La API key no tiene el scope requerido, la IP no está permitida o el workspace está deshabilitado.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"422":{"description":"El workspace no es un espacio personal (`owner_sleep_personal_only`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"429":{"description":"Se superó el límite de solicitudes de la API key (`rate_limited`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}},"post":{"operationId":"setOwnerSleep","tags":["Espacio personal"],"summary":"Avisar que el dueño se durmió o despertó","description":"Pensado para un Atajo de iOS con una automatización de Enfoque: `{\"state\":\"asleep\"}` al activar «Dormir» y `{\"state\":\"awake\"}` al desactivarlo. El aviso vale 12 horas.","security":[{"bearerAuth":[]}],"x-heysteff-scopes":["owner:write"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"state":{"type":"string","enum":["asleep","awake"],"description":"asleep = me fui a dormir; awake = desperté"}},"required":["state"],"additionalProperties":false}}}},"responses":{"200":{"description":"Estado actualizado.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OwnerSleep"}}}},"400":{"description":"El cuerpo o los parámetros no pasan la validación (`validation_error`, con el detalle por campo en `details`) o el JSON es inválido (`invalid_json`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"Falta la API key, es inválida, venció o fue revocada (`unauthorized`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"La API key no tiene el scope requerido, la IP no está permitida o el workspace está deshabilitado.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"422":{"description":"El workspace no es un espacio personal (`owner_sleep_personal_only`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"429":{"description":"Se superó el límite de solicitudes de la API key (`rate_limited`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/v1/webhooks":{"get":{"operationId":"listWebhooks","tags":["Webhooks"],"summary":"Listar webhooks","description":"Los webhooks salientes del workspace, con sus eventos y cómo les va (último 2xx, último fallo, fallos seguidos). Se crean y editan con POST/PATCH/DELETE /v1/webhooks, en Configuración → Desarrolladores o por MCP.","security":[{"bearerAuth":[]}],"x-heysteff-scopes":["webhooks:read"],"responses":{"200":{"description":"Webhooks.","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/Webhook"}}},"required":["data"]}}}},"401":{"description":"Falta la API key, es inválida, venció o fue revocada (`unauthorized`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"La API key no tiene el scope requerido, la IP no está permitida o el workspace está deshabilitado.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"429":{"description":"Se superó el límite de solicitudes de la API key (`rate_limited`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}},"post":{"operationId":"createWebhook","tags":["Webhooks"],"summary":"Crear un webhook","description":"Crea un webhook saliente: `url` (https) y `events` (vacío = todos los eventos). Opcional: `batch` para agrupar los `message:received` rápidos de una conversación en un solo POST (por defecto 5 s y hasta 50 eventos) e `inactivity_hours` para `conversation.inactive` (por defecto 24). La respuesta trae el secreto de firma en `secret` UNA sola vez: guárdalo para verificar `X-Heysteff-Signature`. Hasta 20 por hora por workspace.","security":[{"bearerAuth":[]}],"x-heysteff-scopes":["webhooks:write"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"url":{"type":"string","format":"uri"},"events":{"type":"array","items":{"type":"string"},"default":[]},"format":{"type":"string","enum":["heysteff","meta"],"default":"heysteff"},"batch":{"type":["object","null"],"properties":{"wait_seconds":{"type":"integer","minimum":1,"maximum":60,"default":5},"max_size":{"type":"integer","minimum":1,"maximum":100,"default":50}}},"inactivity_hours":{"type":"integer","minimum":1,"maximum":720}},"required":["url"]}}}},"responses":{"201":{"description":"Webhook creado.","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"url":{"type":"string"},"events":{"type":"array","items":{"type":"string"}},"is_active":{"type":"boolean"},"created_at":{"type":"string","format":"date-time"},"batch":{"allOf":[{"$ref":"#/components/schemas/WebhookBatch"},{"description":"Los `message:received` de una conversación que llegan dentro de `wait_seconds` (1–60) salen en un solo POST con `events: [...]`, o antes si se juntan `max_size` (1–100). Ese POST trae `X-Heysteff-Batch: true` y `X-Heysteff-Batch-Size`."}]},"inactivity_hours":{"type":"integer"},"secret":{"type":"string","description":"Secreto HMAC. Solo se muestra ahora."}},"required":["id","url","events","is_active","created_at","batch","inactivity_hours","secret"]}},"required":["data"]}}}},"400":{"description":"El cuerpo o los parámetros no pasan la validación (`validation_error`, con el detalle por campo en `details`) o el JSON es inválido (`invalid_json`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"Falta la API key, es inválida, venció o fue revocada (`unauthorized`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"402":{"description":"El plan del workspace no incluye webhooks (`requires_web_plan`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"La API key no tiene el scope requerido, la IP no está permitida o el workspace está deshabilitado.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"429":{"description":"Se superó el límite de solicitudes de la API key (`rate_limited`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/v1/webhooks/{id}":{"patch":{"operationId":"updateWebhook","tags":["Webhooks"],"summary":"Editar un webhook","description":"Cambia los eventos (`events`), lo pausa y reactiva (`is_active`), la agrupación (`batch`, `null` la apaga) o las horas de inactividad (`inactivity_hours`). Reactivar un webhook que se pausó solo manda lo que quedó en cola. La URL y el secreto no cambian: para otra URL crea un webhook nuevo y borra este.","security":[{"bearerAuth":[]}],"x-heysteff-scopes":["webhooks:write"],"parameters":[{"schema":{"type":"string","format":"uuid","example":"6f1c2a8e-3b4d-4e5f-9a0b-1c2d3e4f5a6b"},"required":true,"name":"id","in":"path"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"events":{"type":"array","items":{"type":"string"}},"is_active":{"type":"boolean"},"format":{"type":"string","enum":["heysteff","meta"]},"batch":{"type":["object","null"],"properties":{"wait_seconds":{"type":"integer","minimum":1,"maximum":60,"default":5},"max_size":{"type":"integer","minimum":1,"maximum":100,"default":50}}},"inactivity_hours":{"type":"integer","minimum":1,"maximum":720}}}}}},"responses":{"200":{"description":"Webhook actualizado.","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/Webhook"}},"required":["data"]}}}},"400":{"description":"El cuerpo o los parámetros no pasan la validación (`validation_error`, con el detalle por campo en `details`) o el JSON es inválido (`invalid_json`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"Falta la API key, es inválida, venció o fue revocada (`unauthorized`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"La API key no tiene el scope requerido, la IP no está permitida o el workspace está deshabilitado.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"El recurso no existe en este workspace (`not_found`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"429":{"description":"Se superó el límite de solicitudes de la API key (`rate_limited`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}},"delete":{"operationId":"deleteWebhook","tags":["Webhooks"],"summary":"Borrar un webhook","description":"Borra el webhook: deja de recibir eventos.","security":[{"bearerAuth":[]}],"x-heysteff-scopes":["webhooks:write"],"parameters":[{"schema":{"type":"string","format":"uuid","example":"6f1c2a8e-3b4d-4e5f-9a0b-1c2d3e4f5a6b"},"required":true,"name":"id","in":"path"}],"responses":{"200":{"description":"Borrado.","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"deleted":{"type":"boolean","enum":[true]}},"required":["id","deleted"]}},"required":["data"]}}}},"400":{"description":"El id no es un UUID (`invalid_id`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"Falta la API key, es inválida, venció o fue revocada (`unauthorized`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"La API key no tiene el scope requerido, la IP no está permitida o el workspace está deshabilitado.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"El recurso no existe en este workspace (`not_found`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"429":{"description":"Se superó el límite de solicitudes de la API key (`rate_limited`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/v1/webhooks/{id}/deliveries":{"get":{"operationId":"listWebhookDeliveries","tags":["Webhooks"],"summary":"Registro de entregas de un webhook","description":"Entregas de un webhook, de la más nueva a la más vieja, paginadas por cursor. Cada una dice qué evento era, cómo le fue al último intento (status HTTP, duración, motivo del fallo) y un extracto de lo que respondió tu servidor. Una entrega que falla se reintenta sola con espera creciente (1, 2, 4, 8 y 16 minutos) hasta 6 intentos.","security":[{"bearerAuth":[]}],"x-heysteff-scopes":["webhooks:read"],"parameters":[{"schema":{"type":"string","format":"uuid","example":"6f1c2a8e-3b4d-4e5f-9a0b-1c2d3e4f5a6b"},"required":true,"name":"id","in":"path"},{"schema":{"type":"string","enum":["succeeded","failed","pending"],"description":"succeeded = respondió 2xx; failed = el último intento falló (incluye las que se reintentan solas); pending = aún sin intentar."},"required":false,"description":"succeeded = respondió 2xx; failed = el último intento falló (incluye las que se reintentan solas); pending = aún sin intentar.","name":"status","in":"query"},{"schema":{"type":"string","minLength":1,"maxLength":120,"description":"Solo las entregas de ese evento, p. ej. message.received."},"required":false,"description":"Solo las entregas de ese evento, p. ej. message.received.","name":"event","in":"query"},{"schema":{"type":"string","format":"date-time","description":"Creadas desde (ISO 8601, inclusive)."},"required":false,"description":"Creadas desde (ISO 8601, inclusive).","name":"since","in":"query"},{"schema":{"type":"string","format":"date-time","description":"Creadas antes de (ISO 8601, exclusive)."},"required":false,"description":"Creadas antes de (ISO 8601, exclusive).","name":"until","in":"query"},{"schema":{"type":"string","minLength":1,"maxLength":64,"description":"`cursor.next` de la página anterior."},"required":false,"description":"`cursor.next` de la página anterior.","name":"cursor","in":"query"},{"schema":{"type":"integer","minimum":1,"maximum":200},"required":false,"name":"limit","in":"query"}],"responses":{"200":{"description":"Página de entregas.","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/WebhookDelivery"}},"cursor":{"type":"object","properties":{"next":{"type":["string","null"]},"has_more":{"type":"boolean"}},"required":["next","has_more"]}},"required":["data","cursor"]}}}},"400":{"description":"El cuerpo o los parámetros no pasan la validación (`validation_error`, con el detalle por campo en `details`) o el JSON es inválido (`invalid_json`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"Falta la API key, es inválida, venció o fue revocada (`unauthorized`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"La API key no tiene el scope requerido, la IP no está permitida o el workspace está deshabilitado.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"El recurso no existe en este workspace (`not_found`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"429":{"description":"Se superó el límite de solicitudes de la API key (`rate_limited`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/v1/webhook-deliveries/{id}/retry":{"post":{"operationId":"retryWebhookDelivery","tags":["Webhooks"],"summary":"Reintentar una entrega","description":"Vuelve a mandar ahora una entrega que falló (`state` = `failed` o `retrying`): mismo cuerpo y mismo `X-Heysteff-Delivery-Id`, con firma nueva. No reinicia el contador de intentos. Repetir la llamada mientras la entrega espera su turno no la duplica. Hasta 120 reintentos por hora por workspace.","security":[{"bearerAuth":[]}],"x-heysteff-scopes":["webhooks:write"],"parameters":[{"schema":{"type":"string","format":"uuid","example":"6f1c2a8e-3b4d-4e5f-9a0b-1c2d3e4f5a6b"},"required":true,"name":"id","in":"path"}],"responses":{"202":{"description":"Entrega en cola para ahora.","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/WebhookDelivery"}},"required":["data"]}}}},"400":{"description":"El id no es un UUID (`invalid_id`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"Falta la API key, es inválida, venció o fue revocada (`unauthorized`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"402":{"description":"El plan del workspace no incluye webhooks (`requires_web_plan`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"La API key no tiene el scope requerido, la IP no está permitida o el workspace está deshabilitado.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"El recurso no existe en este workspace (`not_found`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"409":{"description":"La entrega ya salió bien o todavía no se intentó (`not_retryable`), o el webhook está pausado (`webhook_inactive`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"429":{"description":"Se superó el límite de solicitudes de la API key (`rate_limited`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/v1/numbers":{"get":{"operationId":"listNumbers","tags":["Números de WhatsApp"],"summary":"Listar los números de heysteff","description":"Los números de WhatsApp que heysteff le dio al workspace (conectados y la reserva viva), con el estado de su nombre visible, el cupo del plan y si se puede pedir otro.","security":[{"bearerAuth":[]}],"x-heysteff-scopes":["numbers:read"],"responses":{"200":{"description":"Números del workspace.","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/WhatsAppNumber"}},"plan":{"type":"object","properties":{"limit":{"type":"integer","description":"Números incluidos en el plan; -1 = ilimitado.","example":1},"used":{"type":"integer","description":"Conectados más la reserva viva.","example":0}},"required":["limit","used"]},"offer":{"type":"object","properties":{"available":{"type":"boolean","description":"Si POST /v1/numbers funcionaría ahora."},"reason":{"type":["string","null"],"enum":["plan_limit","pool_empty","pool_disabled"]}},"required":["available","reason"]}},"required":["data","plan","offer"]}}}},"401":{"description":"Falta la API key, es inválida, venció o fue revocada (`unauthorized`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"La API key no tiene el scope requerido, la IP no está permitida o el workspace está deshabilitado.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"429":{"description":"Se superó el límite de solicitudes de la API key (`rate_limited`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}},"post":{"operationId":"reserveNumber","tags":["Números de WhatsApp"],"summary":"Reservar un número y obtener el link de conexión","description":"Reserva un número verificado del pool de heysteff por 30 minutos y devuelve `connect_url`. Meta solo deja un número en la cuenta de WhatsApp de un negocio dentro de su Embedded Signup: alguien tiene que abrir `connect_url` en un navegador, entrar con Facebook y elegir el número. La API reserva el número y arma ese link; no puede saltarse el paso de Meta. Si ya hay una reserva viva se reusa: el plazo se renueva y el link es nuevo (responde 200 en vez de 201). El número cuenta contra `provisioned_numbers` del plan. Los mensajes los paga tu cuenta de WhatsApp a Meta.","security":[{"bearerAuth":[]}],"x-heysteff-scopes":["numbers:write"],"responses":{"200":{"description":"Reserva existente, renovada.","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/WhatsAppNumberReservation"}},"required":["data"]}}}},"201":{"description":"Reserva nueva.","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/WhatsAppNumberReservation"}},"required":["data"]}}}},"401":{"description":"Falta la API key, es inválida, venció o fue revocada (`unauthorized`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"402":{"description":"Se alcanzó un límite del plan (`plan_limit_exceeded`, con `upgrade_url` en `details`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"La API key no tiene el scope requerido, la IP no está permitida o el workspace está deshabilitado.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"409":{"description":"No hay números disponibles ahora (`wa_pool_empty`) o el servicio está apagado (`wa_pool_disabled`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"429":{"description":"Se superó el límite de solicitudes de la API key (`rate_limited`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/v1/numbers/{id}":{"get":{"operationId":"getNumber","tags":["Números de WhatsApp"],"summary":"Ver un número","description":"Un número de heysteff del workspace. Una reserva vencida ya no aparece (404).","security":[{"bearerAuth":[]}],"x-heysteff-scopes":["numbers:read"],"parameters":[{"schema":{"type":"string","format":"uuid","example":"6f1c2a8e-3b4d-4e5f-9a0b-1c2d3e4f5a6b"},"required":true,"name":"id","in":"path"}],"responses":{"200":{"description":"Número.","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/WhatsAppNumber"}},"required":["data"]}}}},"400":{"description":"El cuerpo o los parámetros no pasan la validación (`validation_error`, con el detalle por campo en `details`) o el JSON es inválido (`invalid_json`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"Falta la API key, es inválida, venció o fue revocada (`unauthorized`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"La API key no tiene el scope requerido, la IP no está permitida o el workspace está deshabilitado.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"El recurso no existe en este workspace (`not_found`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"429":{"description":"Se superó el límite de solicitudes de la API key (`rate_limited`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}},"delete":{"operationId":"releaseNumber","tags":["Números de WhatsApp"],"summary":"Soltar una reserva","description":"Devuelve al pool una reserva que nadie terminó de conectar. Un número ya conectado no se suelta por API: está en tu cuenta de WhatsApp y heysteff lo sigue pagando mientras exista el canal (409 `wa_pool_number_connected`). Tampoco una reserva retenida porque un alta terminó en Meta sin confirmar qué número quedó (409 `wa_pool_number_on_hold`).","security":[{"bearerAuth":[]}],"x-heysteff-scopes":["numbers:write"],"parameters":[{"schema":{"type":"string","format":"uuid","example":"6f1c2a8e-3b4d-4e5f-9a0b-1c2d3e4f5a6b"},"required":true,"name":"id","in":"path"}],"responses":{"200":{"description":"Reserva soltada.","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"released":{"type":"boolean","enum":[true]}},"required":["id","released"]}},"required":["data"]}}}},"400":{"description":"El cuerpo o los parámetros no pasan la validación (`validation_error`, con el detalle por campo en `details`) o el JSON es inválido (`invalid_json`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"Falta la API key, es inválida, venció o fue revocada (`unauthorized`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"La API key no tiene el scope requerido, la IP no está permitida o el workspace está deshabilitado.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"El recurso no existe en este workspace (`not_found`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"409":{"description":"El número ya está conectado (`wa_pool_number_connected`) o la reserva está retenida (`wa_pool_number_on_hold`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"429":{"description":"Se superó el límite de solicitudes de la API key (`rate_limited`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/v1/numbers/{id}/display-name":{"post":{"operationId":"requestNumberDisplayName","tags":["Números de WhatsApp"],"summary":"Pedir un nombre visible","description":"Pide a Meta un nombre visible nuevo (el nombre del negocio que ven tus clientes) para un número conectado. Meta lo revisa y el resultado llega en `display_name.status`. De 3 a 50 caracteres, sin emojis ni links. Al conectar un número, heysteff ya pide el nombre de tu negocio.","security":[{"bearerAuth":[]}],"x-heysteff-scopes":["numbers:write"],"parameters":[{"schema":{"type":"string","format":"uuid","example":"6f1c2a8e-3b4d-4e5f-9a0b-1c2d3e4f5a6b"},"required":true,"name":"id","in":"path"}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/DisplayNameRequest"}}}},"responses":{"202":{"description":"Pedido enviado a Meta; queda en revisión.","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"channel_connection_id":{"type":"string","format":"uuid"},"requested_verified_name":{"type":"string"},"name_status":{"type":"string","enum":["PENDING_REVIEW"]}},"required":["id","channel_connection_id","requested_verified_name","name_status"]}},"required":["data"]}}}},"400":{"description":"El cuerpo o los parámetros no pasan la validación (`validation_error`, con el detalle por campo en `details`) o el JSON es inválido (`invalid_json`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"Falta la API key, es inválida, venció o fue revocada (`unauthorized`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"La API key no tiene el scope requerido, la IP no está permitida o el workspace está deshabilitado.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"El recurso no existe en este workspace (`not_found`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"409":{"description":"El número todavía no está conectado (`wa_pool_number_not_connected`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"424":{"description":"Meta rechazó el pedido (`meta_display_name_rejected`, con `details.meta_code`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"429":{"description":"Se superó el límite de solicitudes de la API key (`rate_limited`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/v1/customers":{"get":{"operationId":"listCustomers","tags":["Clientes y links de conexión"],"summary":"Listar clientes","description":"Los clientes del workspace, del más nuevo al más viejo. Pagina con `limit` y `next_cursor`.","security":[{"bearerAuth":[]}],"x-heysteff-scopes":["customers:read"],"parameters":[{"schema":{"type":"string","description":"Solo el cliente con este external_id."},"required":false,"name":"external_id","in":"query"},{"schema":{"type":"string","description":"Busca por nombre o external_id (contiene, sin distinguir mayúsculas)."},"required":false,"name":"q","in":"query"},{"schema":{"type":"integer","minimum":1,"maximum":100,"description":"Entre 1 y 100 (50 por defecto)."},"required":false,"name":"limit","in":"query"},{"schema":{"type":"string","description":"`next_cursor` de la página anterior."},"required":false,"name":"cursor","in":"query"}],"responses":{"200":{"description":"Clientes.","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/Customer"}},"next_cursor":{"type":["string","null"]}},"required":["data","next_cursor"]}}}},"400":{"description":"El cuerpo o los parámetros no pasan la validación (`validation_error`, con el detalle por campo en `details`) o el JSON es inválido (`invalid_json`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"Falta la API key, es inválida, venció o fue revocada (`unauthorized`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"La API key no tiene el scope requerido, la IP no está permitida o el workspace está deshabilitado.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"429":{"description":"Se superó el límite de solicitudes de la API key (`rate_limited`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}},"post":{"operationId":"createCustomer","tags":["Clientes y links de conexión"],"summary":"Crear un cliente","description":"Crea un cliente: el cliente final de tu SaaS que va a conectar sus propios canales. Developer Free admite 1 cliente, Developer 10 y Developer Platform ilimitados (402 `plan_limit` al pasarse).","security":[{"bearerAuth":[]}],"x-heysteff-scopes":["customers:write"],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"external_id":{"type":"string","minLength":1,"maxLength":200},"name":{"type":"string","minLength":1,"maxLength":200},"metadata":{"type":"object","additionalProperties":{}}},"required":["external_id","name"],"additionalProperties":false}}}},"responses":{"201":{"description":"Cliente creado.","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/Customer"}},"required":["data"]}}}},"400":{"description":"El cuerpo o los parámetros no pasan la validación (`validation_error`, con el detalle por campo en `details`) o el JSON es inválido (`invalid_json`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"Falta la API key, es inválida, venció o fue revocada (`unauthorized`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"402":{"description":"Se alcanzó un límite del plan (`plan_limit_exceeded`, con `upgrade_url` en `details`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"La API key no tiene el scope requerido, la IP no está permitida o el workspace está deshabilitado.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"409":{"description":"Ya hay un cliente con ese `external_id` (`customer_external_id_taken`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"429":{"description":"Se superó el límite de solicitudes de la API key (`rate_limited`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/v1/customers/{id}":{"get":{"operationId":"getCustomer","tags":["Clientes y links de conexión"],"summary":"Ver un cliente","security":[{"bearerAuth":[]}],"x-heysteff-scopes":["customers:read"],"parameters":[{"schema":{"type":"string","format":"uuid","example":"6f1c2a8e-3b4d-4e5f-9a0b-1c2d3e4f5a6b"},"required":true,"name":"id","in":"path"}],"responses":{"200":{"description":"Cliente.","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/Customer"}},"required":["data"]}}}},"400":{"description":"El cuerpo o los parámetros no pasan la validación (`validation_error`, con el detalle por campo en `details`) o el JSON es inválido (`invalid_json`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"Falta la API key, es inválida, venció o fue revocada (`unauthorized`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"La API key no tiene el scope requerido, la IP no está permitida o el workspace está deshabilitado.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"El cliente no existe en este workspace (`customer_not_found`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"429":{"description":"Se superó el límite de solicitudes de la API key (`rate_limited`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}},"patch":{"operationId":"updateCustomer","tags":["Clientes y links de conexión"],"summary":"Editar un cliente","description":"Cambia `name`, `external_id` o `metadata` (manda al menos uno).","security":[{"bearerAuth":[]}],"x-heysteff-scopes":["customers:write"],"parameters":[{"schema":{"type":"string","format":"uuid","example":"6f1c2a8e-3b4d-4e5f-9a0b-1c2d3e4f5a6b"},"required":true,"name":"id","in":"path"}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"external_id":{"type":"string","minLength":1,"maxLength":200},"name":{"type":"string","minLength":1,"maxLength":200},"metadata":{"type":"object","additionalProperties":{}}},"additionalProperties":false}}}},"responses":{"200":{"description":"Cliente editado.","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/Customer"}},"required":["data"]}}}},"400":{"description":"El cuerpo o los parámetros no pasan la validación (`validation_error`, con el detalle por campo en `details`) o el JSON es inválido (`invalid_json`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"Falta la API key, es inválida, venció o fue revocada (`unauthorized`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"La API key no tiene el scope requerido, la IP no está permitida o el workspace está deshabilitado.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"El cliente no existe en este workspace (`customer_not_found`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"409":{"description":"Ya hay un cliente con ese `external_id` (`customer_external_id_taken`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"429":{"description":"Se superó el límite de solicitudes de la API key (`rate_limited`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}},"delete":{"operationId":"deleteCustomer","tags":["Clientes y links de conexión"],"summary":"Borrar un cliente","description":"Borra el cliente y sus links. Sus canales siguen conectados en el workspace, sin cliente: si ya no los usas, desconéctalos aparte.","security":[{"bearerAuth":[]}],"x-heysteff-scopes":["customers:write"],"parameters":[{"schema":{"type":"string","format":"uuid","example":"6f1c2a8e-3b4d-4e5f-9a0b-1c2d3e4f5a6b"},"required":true,"name":"id","in":"path"}],"responses":{"200":{"description":"Cliente borrado.","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"deleted":{"type":"boolean","enum":[true]}},"required":["id","deleted"]}},"required":["data"]}}}},"400":{"description":"El cuerpo o los parámetros no pasan la validación (`validation_error`, con el detalle por campo en `details`) o el JSON es inválido (`invalid_json`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"Falta la API key, es inválida, venció o fue revocada (`unauthorized`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"La API key no tiene el scope requerido, la IP no está permitida o el workspace está deshabilitado.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"El cliente no existe en este workspace (`customer_not_found`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"429":{"description":"Se superó el límite de solicitudes de la API key (`rate_limited`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/v1/customers/{id}/setup-links":{"post":{"operationId":"createCustomerSetupLink","tags":["Clientes y links de conexión"],"summary":"Crear un link de conexión","description":"Devuelve `url`, una página de heysteff donde tu cliente conecta UNO de sus canales (WhatsApp por el Embedded Signup de Meta, Instagram con o sin página de Facebook, Messenger o Gmail) a tu workspace, sin cuenta de heysteff. El link vence a las `expires_in_hours` (720 por defecto, máximo 2160) y queda usado al conectar un canal. Un solo link activo por cliente y canal: crear uno revoca los anteriores que permiten alguno de los mismos canales. Configurable: idioma (`locale`), colores (`theme`), logo y nombre, cómo conectar WhatsApp (`whatsapp_modes`: número propio, coexistencia con la app del celular o un número de heysteff) y `reconnect_channel_id` para volver a conectar un canal caído del cliente. Al terminar, la página vuelve a `success_redirect_url` con `?status=connected&channel_id=…&customer_id=…&channel_type=…` (o a `failure_redirect_url` con `?status=error&error=…`) y tu webhook recibe `customer.channel_connected`. La URL solo viene en esta respuesta.","security":[{"bearerAuth":[]}],"x-heysteff-scopes":["customers:write"],"parameters":[{"schema":{"type":"string","format":"uuid","example":"6f1c2a8e-3b4d-4e5f-9a0b-1c2d3e4f5a6b"},"required":true,"name":"id","in":"path"}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"allowed_channels":{"type":"array","items":{"type":"string","enum":["whatsapp_cloud","instagram","facebook","gmail"]},"minItems":1,"maxItems":4},"expires_in_hours":{"type":"integer","minimum":1,"maximum":2160},"success_redirect_url":{"type":"string","maxLength":2000},"failure_redirect_url":{"type":"string","maxLength":2000},"brand_name":{"type":"string","minLength":1,"maxLength":120},"brand_logo_url":{"type":"string","maxLength":2000},"locale":{"type":"string","enum":["auto","es","en","pt"]},"theme":{"type":"object","properties":{"primary_color":{"type":"string","pattern":"^#[0-9a-fA-F]{6}$"},"background_color":{"type":"string","pattern":"^#[0-9a-fA-F]{6}$"},"text_color":{"type":"string","pattern":"^#[0-9a-fA-F]{6}$"},"border_color":{"type":"string","pattern":"^#[0-9a-fA-F]{6}$"}},"additionalProperties":false},"whatsapp_modes":{"type":"array","items":{"type":"string","enum":["own_number","coexistence","heysteff_number"]},"minItems":1,"maxItems":3},"reconnect_channel_id":{"type":"string","format":"uuid"}},"additionalProperties":false}}}},"responses":{"201":{"description":"Link creado.","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/CustomerSetupLink"}},"required":["data"]}}}},"400":{"description":"El cuerpo o los parámetros no pasan la validación (`validation_error`, con el detalle por campo en `details`) o el JSON es inválido (`invalid_json`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"Falta la API key, es inválida, venció o fue revocada (`unauthorized`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"402":{"description":"Se alcanzó un límite del plan (`plan_limit_exceeded`, con `upgrade_url` en `details`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"La API key no tiene el scope requerido, la IP no está permitida o el workspace está deshabilitado.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"El cliente no existe en este workspace (`customer_not_found`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"422":{"description":"`reconnect_channel_id` no es un canal de este cliente (`reconnect_channel_not_found`) o no se puede reconectar con un link.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"429":{"description":"Se superó el límite de solicitudes de la API key (`rate_limited`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}},"get":{"operationId":"listCustomerSetupLinks","tags":["Clientes y links de conexión"],"summary":"Links de un cliente","description":"Los últimos 50 links del cliente con su estado (`active`, `used`, `expired`, `revoked`). Sin la URL: solo existe al crear el link.","security":[{"bearerAuth":[]}],"x-heysteff-scopes":["customers:read"],"parameters":[{"schema":{"type":"string","format":"uuid","example":"6f1c2a8e-3b4d-4e5f-9a0b-1c2d3e4f5a6b"},"required":true,"name":"id","in":"path"}],"responses":{"200":{"description":"Links del cliente.","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/CustomerSetupLinkStored"}}},"required":["data"]}}}},"400":{"description":"El cuerpo o los parámetros no pasan la validación (`validation_error`, con el detalle por campo en `details`) o el JSON es inválido (`invalid_json`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"Falta la API key, es inválida, venció o fue revocada (`unauthorized`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"La API key no tiene el scope requerido, la IP no está permitida o el workspace está deshabilitado.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"El cliente no existe en este workspace (`customer_not_found`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"429":{"description":"Se superó el límite de solicitudes de la API key (`rate_limited`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/v1/setup-links":{"get":{"operationId":"listSetupLinks","tags":["Clientes y links de conexión"],"summary":"Links de conexión del workspace","description":"Todos los links del workspace, del más nuevo al más viejo, con su estado y el cliente. Filtra por `status` y `customer_id`.","security":[{"bearerAuth":[]}],"x-heysteff-scopes":["customers:read"],"parameters":[{"schema":{"type":"string","enum":["active","used","expired","revoked"]},"required":false,"name":"status","in":"query"},{"schema":{"type":"string","format":"uuid"},"required":false,"name":"customer_id","in":"query"},{"schema":{"type":"integer","minimum":1,"maximum":100,"description":"Entre 1 y 100 (50 por defecto)."},"required":false,"name":"limit","in":"query"}],"responses":{"200":{"description":"Links.","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/SetupLinkListItem"}}},"required":["data"]}}}},"400":{"description":"El cuerpo o los parámetros no pasan la validación (`validation_error`, con el detalle por campo en `details`) o el JSON es inválido (`invalid_json`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"Falta la API key, es inválida, venció o fue revocada (`unauthorized`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"La API key no tiene el scope requerido, la IP no está permitida o el workspace está deshabilitado.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"429":{"description":"Se superó el límite de solicitudes de la API key (`rate_limited`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/v1/setup-links/{id}/revoke":{"post":{"operationId":"revokeSetupLink","tags":["Clientes y links de conexión"],"summary":"Revocar un link de conexión","description":"La página del link deja de funcionar. Revocar dos veces no hace nada; un link ya usado responde 409 `setup_link_used`.","security":[{"bearerAuth":[]}],"x-heysteff-scopes":["customers:write"],"parameters":[{"schema":{"type":"string","format":"uuid","example":"6f1c2a8e-3b4d-4e5f-9a0b-1c2d3e4f5a6b"},"required":true,"name":"id","in":"path"}],"responses":{"200":{"description":"Link revocado.","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/CustomerSetupLinkStored"}},"required":["data"]}}}},"401":{"description":"Falta la API key, es inválida, venció o fue revocada (`unauthorized`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"La API key no tiene el scope requerido, la IP no está permitida o el workspace está deshabilitado.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"El link no existe en este workspace (`setup_link_not_found`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"409":{"description":"El link ya se usó (`setup_link_used`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"429":{"description":"Se superó el límite de solicitudes de la API key (`rate_limited`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/v1/customers/{id}/usage":{"get":{"operationId":"getCustomerUsage","tags":["Clientes y links de conexión"],"summary":"Uso de un cliente","description":"Mensajes (totales, entrantes, salientes y fallidos) y conversaciones de los canales que conectó el cliente, en total, por canal y por día (UTC). Sin fechas, los últimos 30 días; el rango puede abarcar hasta 366 días. Sirve para cobrarle a tus clientes.","security":[{"bearerAuth":[]}],"x-heysteff-scopes":["customers:read"],"parameters":[{"schema":{"type":"string","format":"uuid","example":"6f1c2a8e-3b4d-4e5f-9a0b-1c2d3e4f5a6b"},"required":true,"name":"id","in":"path"},{"schema":{"type":"string","description":"Primer día (YYYY-MM-DD, UTC), incluido. Por defecto, 29 días antes de `to`."},"required":false,"name":"from","in":"query"},{"schema":{"type":"string","description":"Último día (YYYY-MM-DD, UTC), incluido. Por defecto, hoy."},"required":false,"name":"to","in":"query"}],"responses":{"200":{"description":"Uso del cliente.","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/CustomerUsage"}},"required":["data"]}}}},"400":{"description":"El cuerpo o los parámetros no pasan la validación (`validation_error`, con el detalle por campo en `details`) o el JSON es inválido (`invalid_json`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"Falta la API key, es inválida, venció o fue revocada (`unauthorized`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"La API key no tiene el scope requerido, la IP no está permitida o el workspace está deshabilitado.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"El cliente no existe en este workspace (`customer_not_found`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"429":{"description":"Se superó el límite de solicitudes de la API key (`rate_limited`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/v1/customers/{id}/channels":{"get":{"operationId":"listCustomerChannels","tags":["Clientes y links de conexión"],"summary":"Canales de un cliente","description":"Los canales que conectó el cliente con sus links, activos y desconectados.","security":[{"bearerAuth":[]}],"x-heysteff-scopes":["customers:read"],"parameters":[{"schema":{"type":"string","format":"uuid","example":"6f1c2a8e-3b4d-4e5f-9a0b-1c2d3e4f5a6b"},"required":true,"name":"id","in":"path"}],"responses":{"200":{"description":"Canales del cliente.","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/CustomerChannel"}}},"required":["data"]}}}},"400":{"description":"El cuerpo o los parámetros no pasan la validación (`validation_error`, con el detalle por campo en `details`) o el JSON es inválido (`invalid_json`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"Falta la API key, es inválida, venció o fue revocada (`unauthorized`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"La API key no tiene el scope requerido, la IP no está permitida o el workspace está deshabilitado.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"El cliente no existe en este workspace (`customer_not_found`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"429":{"description":"Se superó el límite de solicitudes de la API key (`rate_limited`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/v1/whatsapp-flows":{"get":{"operationId":"listWhatsAppFlows","tags":["WhatsApp Flows"],"summary":"Listar los WhatsApp Flows","description":"Los Flows de las cuentas de WhatsApp Cloud del espacio, del más reciente al más viejo (hasta 500), desde el espejo de heysteff. Si creaste Flows en WhatsApp Manager, primero POST /v1/whatsapp-flows/sync. Solo WhatsApp Cloud. Solo con una key `sk_live_`: una key de prueba responde 403 `test_key_not_allowed` (el número compartido del sandbox no tiene Flows propios).","security":[{"bearerAuth":[]}],"x-heysteff-scopes":["flows:read"],"parameters":[{"schema":{"type":"string","format":"uuid"},"required":false,"name":"channel_connection_id","in":"query"},{"schema":{"type":"string"},"required":false,"name":"status","in":"query"}],"responses":{"200":{"description":"Flows.","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/WhatsAppFlow"}}},"required":["data"]}}}},"400":{"description":"El cuerpo o los parámetros no pasan la validación (`validation_error`, con el detalle por campo en `details`) o el JSON es inválido (`invalid_json`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"Falta la API key, es inválida, venció o fue revocada (`unauthorized`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"La API key no tiene el scope requerido, la IP no está permitida o el workspace está deshabilitado.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"429":{"description":"Se superó el límite de solicitudes de la API key (`rate_limited`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}},"post":{"operationId":"createWhatsAppFlow","tags":["WhatsApp Flows"],"summary":"Crear un Flow","description":"Crea un Flow en borrador en la cuenta de WhatsApp del número (`channel_connection_id`, o el número de WhatsApp Cloud conectado más reciente). `flow_json` es opcional (objeto o texto, hasta 1 MB): se puede subir después con PUT /v1/whatsapp-flows/{id}/json. Con `publish: true` Meta lo publica al crearlo si el Flow JSON no tiene errores. Responde 201 con el Flow y sus `validation_errors`. `save_to_contact: true` guarda cada campo simple de las respuestas en el campo del contacto con la misma clave. Solo con una key `sk_live_`: una key de prueba responde 403 `test_key_not_allowed` (el número compartido del sandbox no tiene Flows propios).","security":[{"bearerAuth":[]}],"x-heysteff-scopes":["flows:write"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"channel_connection_id":{"type":"string","format":"uuid"},"name":{"type":"string","minLength":1,"maxLength":200},"categories":{"type":"array","items":{"type":"string","enum":["SIGN_UP","SIGN_IN","APPOINTMENT_BOOKING","LEAD_GENERATION","CONTACT_US","CUSTOMER_SUPPORT","SURVEY","OTHER"]},"minItems":1,"maxItems":8},"flow_json":{"anyOf":[{"type":"string","minLength":2},{"type":"object","additionalProperties":{}}]},"publish":{"type":"boolean"},"save_to_contact":{"type":"boolean"}},"required":["name","categories"]}}}},"responses":{"201":{"description":"Flow creado.","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/WhatsAppFlowDetail"}},"required":["data"]}}}},"400":{"description":"El cuerpo o los parámetros no pasan la validación (`validation_error`, con el detalle por campo en `details`) o el JSON es inválido (`invalid_json`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"Falta la API key, es inválida, venció o fue revocada (`unauthorized`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"La API key no tiene el scope requerido, la IP no está permitida o el workspace está deshabilitado.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"El `channel_connection_id` no existe o no es un número de WhatsApp Cloud activo (`not_found`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"422":{"description":"El espacio no tiene un número de WhatsApp Cloud conectado (`no_whatsapp_cloud_channel`), o Meta rechazó el Flow JSON (`flow_json_invalid`, con `details.validation_errors` como los devuelve Meta: `error`, `error_type`, `message`, línea, columna y `pointers`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"424":{"description":"Meta rechazó la operación o no respondió (`meta_flow_error`, con `details.meta_code` y `details.meta_status`). El token del número necesita el permiso `whatsapp_business_management`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"429":{"description":"Se superó el límite de solicitudes de la API key (`rate_limited`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/v1/whatsapp-flows/sync":{"post":{"operationId":"syncWhatsAppFlows","tags":["WhatsApp Flows"],"summary":"Sincronizar los Flows desde Meta","description":"Trae de Meta los Flows de la cuenta de WhatsApp de un número (`channel_connection_id`, o el más reciente), los crea o actualiza en heysteff y borra del espejo los que ya no existen en esa cuenta. Úsalo después de crear o editar Flows en WhatsApp Manager. Solo con una key `sk_live_`: una key de prueba responde 403 `test_key_not_allowed` (el número compartido del sandbox no tiene Flows propios).","security":[{"bearerAuth":[]}],"x-heysteff-scopes":["flows:write"],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"channel_connection_id":{"type":"string","format":"uuid"}}}}}},"responses":{"200":{"description":"Sincronizado.","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"channel_connection_id":{"type":"string","format":"uuid"},"synced":{"type":"integer"},"removed":{"type":"integer"}},"required":["channel_connection_id","synced","removed"]}},"required":["data"]}}}},"400":{"description":"El cuerpo o los parámetros no pasan la validación (`validation_error`, con el detalle por campo en `details`) o el JSON es inválido (`invalid_json`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"Falta la API key, es inválida, venció o fue revocada (`unauthorized`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"La API key no tiene el scope requerido, la IP no está permitida o el workspace está deshabilitado.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"El `channel_connection_id` no existe o no está activo (`not_found`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"422":{"description":"El espacio no tiene un número de WhatsApp Cloud conectado (`no_whatsapp_cloud_channel`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"424":{"description":"Meta rechazó la operación o no respondió (`meta_flow_error`, con `details.meta_code` y `details.meta_status`). El token del número necesita el permiso `whatsapp_business_management`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"429":{"description":"Se superó el límite de solicitudes de la API key (`rate_limited`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/v1/whatsapp-flows/responses":{"get":{"operationId":"listWhatsAppFlowResponses","tags":["WhatsApp Flows"],"summary":"Listar las respuestas de los Flows","description":"Las respuestas que enviaron los clientes, la más nueva primero. `flow_id` (el id heysteff del Flow) filtra; para la página siguiente, manda `before` con el `next_before` de la respuesta (null = no hay más). Cada respuesta también llega al momento en el webhook `message:received` con `flow_response`. Solo con una key `sk_live_`: una key de prueba responde 403 `test_key_not_allowed` (el número compartido del sandbox no tiene Flows propios).","security":[{"bearerAuth":[]}],"x-heysteff-scopes":["flows:read"],"parameters":[{"schema":{"type":"string","format":"uuid"},"required":false,"name":"flow_id","in":"query"},{"schema":{"type":"integer","minimum":1,"maximum":100,"default":50},"required":false,"name":"limit","in":"query"},{"schema":{"type":"string","format":"date-time"},"required":false,"name":"before","in":"query"}],"responses":{"200":{"description":"Respuestas.","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/WhatsAppFlowResponse"}},"next_before":{"type":["string","null"],"format":"date-time"}},"required":["data","next_before"]}}}},"400":{"description":"El cuerpo o los parámetros no pasan la validación (`validation_error`, con el detalle por campo en `details`) o el JSON es inválido (`invalid_json`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"Falta la API key, es inválida, venció o fue revocada (`unauthorized`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"La API key no tiene el scope requerido, la IP no está permitida o el workspace está deshabilitado.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"429":{"description":"Se superó el límite de solicitudes de la API key (`rate_limited`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/v1/whatsapp-flows/{id}":{"get":{"operationId":"getWhatsAppFlow","tags":["WhatsApp Flows"],"summary":"Ver un Flow","description":"Un Flow con su estado, sus errores de validación y el último Flow JSON subido desde heysteff (texto). Solo con una key `sk_live_`: una key de prueba responde 403 `test_key_not_allowed` (el número compartido del sandbox no tiene Flows propios).","security":[{"bearerAuth":[]}],"x-heysteff-scopes":["flows:read"],"parameters":[{"schema":{"type":"string","format":"uuid","example":"6f1c2a8e-3b4d-4e5f-9a0b-1c2d3e4f5a6b"},"required":true,"name":"id","in":"path"}],"responses":{"200":{"description":"Flow.","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/WhatsAppFlowDetail"}},"required":["data"]}}}},"400":{"description":"El id no es un UUID (`invalid_id`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"Falta la API key, es inválida, venció o fue revocada (`unauthorized`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"La API key no tiene el scope requerido, la IP no está permitida o el workspace está deshabilitado.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"El recurso no existe en este workspace (`not_found`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"429":{"description":"Se superó el límite de solicitudes de la API key (`rate_limited`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}},"patch":{"operationId":"updateWhatsAppFlow","tags":["WhatsApp Flows"],"summary":"Cambiar un Flow","description":"Cambia el nombre o las categorías (en Meta) y `save_to_contact` (en heysteff). Para cambiar las pantallas, sube otro Flow JSON con PUT /v1/whatsapp-flows/{id}/json. Solo con una key `sk_live_`: una key de prueba responde 403 `test_key_not_allowed` (el número compartido del sandbox no tiene Flows propios).","security":[{"bearerAuth":[]}],"x-heysteff-scopes":["flows:write"],"parameters":[{"schema":{"type":"string","format":"uuid","example":"6f1c2a8e-3b4d-4e5f-9a0b-1c2d3e4f5a6b"},"required":true,"name":"id","in":"path"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string","minLength":1,"maxLength":200},"categories":{"type":"array","items":{"type":"string","enum":["SIGN_UP","SIGN_IN","APPOINTMENT_BOOKING","LEAD_GENERATION","CONTACT_US","CUSTOMER_SUPPORT","SURVEY","OTHER"]},"minItems":1,"maxItems":8},"save_to_contact":{"type":"boolean"}}}}}},"responses":{"200":{"description":"Flow actualizado.","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/WhatsAppFlowDetail"}},"required":["data"]}}}},"400":{"description":"El cuerpo o los parámetros no pasan la validación (`validation_error`, con el detalle por campo en `details`) o el JSON es inválido (`invalid_json`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"Falta la API key, es inválida, venció o fue revocada (`unauthorized`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"La API key no tiene el scope requerido, la IP no está permitida o el workspace está deshabilitado.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"El recurso no existe en este workspace (`not_found`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"424":{"description":"Meta rechazó la operación o no respondió (`meta_flow_error`, con `details.meta_code` y `details.meta_status`). El token del número necesita el permiso `whatsapp_business_management`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"429":{"description":"Se superó el límite de solicitudes de la API key (`rate_limited`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}},"delete":{"operationId":"deleteWhatsAppFlow","tags":["WhatsApp Flows"],"summary":"Borrar un borrador","description":"Borra un Flow en borrador en Meta y en heysteff. Meta solo borra borradores: uno publicado responde 409 `flow_not_draft` (deprécalo con POST /v1/whatsapp-flows/{id}/deprecate). Solo con una key `sk_live_`: una key de prueba responde 403 `test_key_not_allowed` (el número compartido del sandbox no tiene Flows propios).","security":[{"bearerAuth":[]}],"x-heysteff-scopes":["flows:write"],"parameters":[{"schema":{"type":"string","format":"uuid","example":"6f1c2a8e-3b4d-4e5f-9a0b-1c2d3e4f5a6b"},"required":true,"name":"id","in":"path"}],"responses":{"200":{"description":"Borrado.","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"action":{"type":"string","enum":["publish","deprecate","delete"]},"status":{"type":["string","null"],"description":"`PUBLISHED`, `DEPRECATED`, o null si se borró."},"deleted":{"type":"boolean"}},"required":["id","action","status","deleted"]}},"required":["data"]}}}},"400":{"description":"El id no es un UUID (`invalid_id`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"Falta la API key, es inválida, venció o fue revocada (`unauthorized`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"La API key no tiene el scope requerido, la IP no está permitida o el workspace está deshabilitado.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"El recurso no existe en este workspace (`not_found`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"409":{"description":"El Flow no está en borrador (`flow_not_draft`): un Flow publicado no cambia su JSON, no se vuelve a publicar ni se borra (deprécalo).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"424":{"description":"Meta rechazó la operación o no respondió (`meta_flow_error`, con `details.meta_code` y `details.meta_status`). El token del número necesita el permiso `whatsapp_business_management`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"429":{"description":"Se superó el límite de solicitudes de la API key (`rate_limited`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/v1/whatsapp-flows/{id}/json":{"get":{"operationId":"getWhatsAppFlowJson","tags":["WhatsApp Flows"],"summary":"Descargar el Flow JSON","description":"El Flow JSON como objeto: el último subido desde heysteff o, si no hay (Flow creado en WhatsApp Manager), el que tiene Meta. Solo con una key `sk_live_`: una key de prueba responde 403 `test_key_not_allowed` (el número compartido del sandbox no tiene Flows propios).","security":[{"bearerAuth":[]}],"x-heysteff-scopes":["flows:read"],"parameters":[{"schema":{"type":"string","format":"uuid","example":"6f1c2a8e-3b4d-4e5f-9a0b-1c2d3e4f5a6b"},"required":true,"name":"id","in":"path"}],"responses":{"200":{"description":"Flow JSON.","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"flow_json":{"type":["object","null"],"additionalProperties":{}}},"required":["id","flow_json"]}},"required":["data"]}}}},"400":{"description":"El id no es un UUID (`invalid_id`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"Falta la API key, es inválida, venció o fue revocada (`unauthorized`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"La API key no tiene el scope requerido, la IP no está permitida o el workspace está deshabilitado.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"El recurso no existe en este workspace (`not_found`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"424":{"description":"Meta rechazó la operación o no respondió (`meta_flow_error`, con `details.meta_code` y `details.meta_status`). El token del número necesita el permiso `whatsapp_business_management`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"429":{"description":"Se superó el límite de solicitudes de la API key (`rate_limited`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}},"put":{"operationId":"uploadWhatsAppFlowJson","tags":["WhatsApp Flows"],"summary":"Subir el Flow JSON","description":"Sube (reemplaza) el Flow JSON de un borrador: objeto o texto con un objeto JSON, hasta 1 MB. Responde el Flow con los `validation_errors` de Meta; vacío = listo para publicar. Un Flow publicado no cambia (409 `flow_not_draft`): crea otro. Solo con una key `sk_live_`: una key de prueba responde 403 `test_key_not_allowed` (el número compartido del sandbox no tiene Flows propios).","security":[{"bearerAuth":[]}],"x-heysteff-scopes":["flows:write"],"parameters":[{"schema":{"type":"string","format":"uuid","example":"6f1c2a8e-3b4d-4e5f-9a0b-1c2d3e4f5a6b"},"required":true,"name":"id","in":"path"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"flow_json":{"anyOf":[{"type":"string","minLength":2},{"type":"object","additionalProperties":{}}]}},"required":["flow_json"]}}}},"responses":{"200":{"description":"Flow JSON subido.","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/WhatsAppFlowDetail"}},"required":["data"]}}}},"400":{"description":"El cuerpo o los parámetros no pasan la validación (`validation_error`, con el detalle por campo en `details`) o el JSON es inválido (`invalid_json`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"Falta la API key, es inválida, venció o fue revocada (`unauthorized`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"La API key no tiene el scope requerido, la IP no está permitida o el workspace está deshabilitado.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"El recurso no existe en este workspace (`not_found`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"409":{"description":"El Flow no está en borrador (`flow_not_draft`): un Flow publicado no cambia su JSON, no se vuelve a publicar ni se borra (deprécalo).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"422":{"description":"`flow_json` no es un objeto JSON o pesa más de 1 MB, o Meta lo rechazó (`flow_json_invalid`, con `details.validation_errors`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"424":{"description":"Meta rechazó la operación o no respondió (`meta_flow_error`, con `details.meta_code` y `details.meta_status`). El token del número necesita el permiso `whatsapp_business_management`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"429":{"description":"Se superó el límite de solicitudes de la API key (`rate_limited`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/v1/whatsapp-flows/{id}/publish":{"post":{"operationId":"publishWhatsAppFlow","tags":["WhatsApp Flows"],"summary":"Publicar un Flow","description":"Publica un borrador: desde ahí se puede mandar a cualquier cliente y ya no cambia. Meta lo rechaza si el Flow JSON tiene errores (424 `meta_flow_error`, revisa `validation_errors` con GET /v1/whatsapp-flows/{id}). Solo con una key `sk_live_`: una key de prueba responde 403 `test_key_not_allowed` (el número compartido del sandbox no tiene Flows propios).","security":[{"bearerAuth":[]}],"x-heysteff-scopes":["flows:write"],"parameters":[{"schema":{"type":"string","format":"uuid","example":"6f1c2a8e-3b4d-4e5f-9a0b-1c2d3e4f5a6b"},"required":true,"name":"id","in":"path"}],"responses":{"200":{"description":"Publicado.","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"action":{"type":"string","enum":["publish","deprecate","delete"]},"status":{"type":["string","null"],"description":"`PUBLISHED`, `DEPRECATED`, o null si se borró."},"deleted":{"type":"boolean"}},"required":["id","action","status","deleted"]}},"required":["data"]}}}},"400":{"description":"El id no es un UUID (`invalid_id`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"Falta la API key, es inválida, venció o fue revocada (`unauthorized`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"La API key no tiene el scope requerido, la IP no está permitida o el workspace está deshabilitado.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"El recurso no existe en este workspace (`not_found`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"409":{"description":"El Flow no está en borrador (`flow_not_draft`): un Flow publicado no cambia su JSON, no se vuelve a publicar ni se borra (deprécalo).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"424":{"description":"Meta rechazó la operación o no respondió (`meta_flow_error`, con `details.meta_code` y `details.meta_status`). El token del número necesita el permiso `whatsapp_business_management`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"429":{"description":"Se superó el límite de solicitudes de la API key (`rate_limited`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/v1/whatsapp-flows/{id}/deprecate":{"post":{"operationId":"deprecateWhatsAppFlow","tags":["WhatsApp Flows"],"summary":"Deprecar un Flow","description":"Deja de usar un Flow publicado: ya no se puede mandar ni abrir. No se puede deshacer. Solo con una key `sk_live_`: una key de prueba responde 403 `test_key_not_allowed` (el número compartido del sandbox no tiene Flows propios).","security":[{"bearerAuth":[]}],"x-heysteff-scopes":["flows:write"],"parameters":[{"schema":{"type":"string","format":"uuid","example":"6f1c2a8e-3b4d-4e5f-9a0b-1c2d3e4f5a6b"},"required":true,"name":"id","in":"path"}],"responses":{"200":{"description":"Deprecado.","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"action":{"type":"string","enum":["publish","deprecate","delete"]},"status":{"type":["string","null"],"description":"`PUBLISHED`, `DEPRECATED`, o null si se borró."},"deleted":{"type":"boolean"}},"required":["id","action","status","deleted"]}},"required":["data"]}}}},"400":{"description":"El id no es un UUID (`invalid_id`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"Falta la API key, es inválida, venció o fue revocada (`unauthorized`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"La API key no tiene el scope requerido, la IP no está permitida o el workspace está deshabilitado.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"El recurso no existe en este workspace (`not_found`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"409":{"description":"El Flow no está publicado (`flow_not_published`): un borrador se borra con DELETE.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"424":{"description":"Meta rechazó la operación o no respondió (`meta_flow_error`, con `details.meta_code` y `details.meta_status`). El token del número necesita el permiso `whatsapp_business_management`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"429":{"description":"Se superó el límite de solicitudes de la API key (`rate_limited`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/v1/whatsapp-flows/{id}/preview":{"get":{"operationId":"getWhatsAppFlowPreview","tags":["WhatsApp Flows"],"summary":"Link de vista previa","description":"El link de vista previa de Meta: abre el formulario en el navegador para probarlo sin mandarlo. Vence a los 30 días; `invalidate=true` pide uno nuevo. Solo con una key `sk_live_`: una key de prueba responde 403 `test_key_not_allowed` (el número compartido del sandbox no tiene Flows propios).","security":[{"bearerAuth":[]}],"x-heysteff-scopes":["flows:read"],"parameters":[{"schema":{"type":"string","format":"uuid","example":"6f1c2a8e-3b4d-4e5f-9a0b-1c2d3e4f5a6b"},"required":true,"name":"id","in":"path"},{"schema":{"type":"string","enum":["true","false"],"description":"`true` invalida el link anterior y genera otro."},"required":false,"name":"invalidate","in":"query"}],"responses":{"200":{"description":"Vista previa.","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"flow_id":{"type":"string"},"preview_url":{"type":["string","null"]},"expires_at":{"type":["string","null"],"format":"date-time"}},"required":["id","flow_id","preview_url","expires_at"]}},"required":["data"]}}}},"400":{"description":"El id no es un UUID (`invalid_id`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"Falta la API key, es inválida, venció o fue revocada (`unauthorized`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"La API key no tiene el scope requerido, la IP no está permitida o el workspace está deshabilitado.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"El recurso no existe en este workspace (`not_found`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"424":{"description":"Meta rechazó la operación o no respondió (`meta_flow_error`, con `details.meta_code` y `details.meta_status`). El token del número necesita el permiso `whatsapp_business_management`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"429":{"description":"Se superó el límite de solicitudes de la API key (`rate_limited`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/v1/calls":{"get":{"operationId":"listCalls","tags":["Llamadas de WhatsApp"],"summary":"Registro de llamadas","description":"Las llamadas de WhatsApp de tus números de WhatsApp Cloud, la más nueva primero. Filtros: `channel_connection_id`, `status` y `direction`; para la página siguiente, manda `before` con el `next_before` de la respuesta (null = no hay más). Cada llamada también deja una nota en la conversación y avisa por los webhooks `call.started`, `call.ended` y `call.missed`. heysteff registra las llamadas pero no las contesta: no hay audio en esta versión. Solo con una key `sk_live_`: una key de prueba responde 403 `test_key_not_allowed` (el número compartido del sandbox no es tuyo).","security":[{"bearerAuth":[]}],"x-heysteff-scopes":["calls:read"],"parameters":[{"schema":{"type":"string","format":"uuid"},"required":false,"name":"channel_connection_id","in":"query"},{"schema":{"type":"string","enum":["ringing","answered","completed","missed","rejected","failed"]},"required":false,"name":"status","in":"query"},{"schema":{"type":"string","enum":["inbound","outbound"]},"required":false,"name":"direction","in":"query"},{"schema":{"type":"integer","minimum":1,"maximum":100,"default":50},"required":false,"name":"limit","in":"query"},{"schema":{"type":"string","format":"date-time"},"required":false,"name":"before","in":"query"}],"responses":{"200":{"description":"Llamadas.","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/WhatsAppCall"}},"next_before":{"type":["string","null"],"format":"date-time"}},"required":["data","next_before"]}}}},"400":{"description":"El cuerpo o los parámetros no pasan la validación (`validation_error`, con el detalle por campo en `details`) o el JSON es inválido (`invalid_json`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"Falta la API key, es inválida, venció o fue revocada (`unauthorized`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"La API key no tiene el scope requerido, la IP no está permitida o el workspace está deshabilitado.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"429":{"description":"Se superó el límite de solicitudes de la API key (`rate_limited`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}},"post":{"operationId":"startAgentCall","tags":["Llamadas de WhatsApp"],"summary":"Que el agente IA llame a un cliente","description":"El agente IA llama al cliente de una conversación de WhatsApp Cloud y habla con la voz, el conocimiento y las herramientas del espacio; la llamada queda en la conversación con su transcripción y se cobra por minuto igual que una entrante. Exige las llamadas del agente encendidas (módulo Llamadas). `reason` es lo que el agente dice al saludar (\"te llamo porque…\"); `at` (ISO 8601 con zona, hasta 30 días) la programa. WhatsApp exige el permiso del cliente: si no lo dio, le llega un mensaje para permitir la llamada (solo dentro de la ventana de 24 h) y la llamada sale sola cuando acepta. `action`: `calling`, `scheduled`, `permission_requested` o `already_pending` (ya había una pendiente). Errores: 409 `voice_outbound_unavailable` o `voice_call_permission_window_closed`, 422 `voice_call_whatsapp_cloud_only`, 429 `voice_call_permission_limit` o `voice_call_limit` (topes de WhatsApp, con `details.until`), 424 `voice_permission_check_failed`. Solo con una key `sk_live_`: una key de prueba responde 403 `test_key_not_allowed` (el número compartido del sandbox no es tuyo).","security":[{"bearerAuth":[]}],"x-heysteff-scopes":["calls:write"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"conversation_id":{"type":"string","format":"uuid"},"reason":{"type":"string","minLength":3,"maxLength":500},"at":{"type":"string","format":"date-time"}},"required":["conversation_id","reason"],"additionalProperties":false}}}},"responses":{"201":{"description":"Llamada pedida.","content":{"application/json":{"schema":{"type":"object","properties":{"action":{"type":"string","enum":["calling","scheduled","permission_requested","already_pending"]},"request":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"conversation_id":{"type":"string","format":"uuid"},"status":{"type":"string","enum":["awaiting_permission","scheduled","queued","dialing","done","failed","cancelled"]},"reason":{"type":"string"},"call_at":{"type":"string"},"source":{"type":"string"},"voice_call_id":{"type":["string","null"],"format":"uuid"},"error":{"type":["string","null"]}},"required":["id","conversation_id","status","reason","call_at","source","voice_call_id","error"]}},"required":["action","request"]}}}},"400":{"description":"El cuerpo o los parámetros no pasan la validación (`validation_error`, con el detalle por campo en `details`) o el JSON es inválido (`invalid_json`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"Falta la API key, es inválida, venció o fue revocada (`unauthorized`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"La API key no tiene el scope requerido, la IP no está permitida o el workspace está deshabilitado.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"429":{"description":"Se superó el límite de solicitudes de la API key (`rate_limited`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/v1/whatsapp-numbers/{id}/calling":{"get":{"operationId":"getWhatsAppCallingSettings","tags":["Llamadas de WhatsApp"],"summary":"Ver los ajustes de llamadas","description":"Los ajustes de llamadas del número, leídos en vivo de Meta: si están encendidas, el botón de llamar, el permiso de devolución y el horario. Solo con una key `sk_live_`: una key de prueba responde 403 `test_key_not_allowed` (el número compartido del sandbox no es tuyo).","security":[{"bearerAuth":[]}],"x-heysteff-scopes":["calls:read"],"parameters":[{"schema":{"type":"string","format":"uuid","example":"6f1c2a8e-3b4d-4e5f-9a0b-1c2d3e4f5a6b"},"required":true,"description":"`channel_connection_id` del número de WhatsApp Cloud (GET /v1/numbers o la consola).","name":"id","in":"path"}],"responses":{"200":{"description":"Ajustes.","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/WhatsAppCallingSettings"}},"required":["data"]}}}},"400":{"description":"El id no es un UUID (`invalid_id`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"Falta la API key, es inválida, venció o fue revocada (`unauthorized`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"La API key no tiene el scope requerido, la IP no está permitida o el workspace está deshabilitado.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"No hay un número de WhatsApp Cloud activo con ese `channel_connection_id` en este workspace (`not_found`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"424":{"description":"Meta rechazó la operación o no respondió (`meta_error`, con `details.meta_code`). Algunos ajustes exigen un número dedicado de WhatsApp Cloud (no en coexistencia) y que Meta lo habilite.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"429":{"description":"Se superó el límite de solicitudes de la API key (`rate_limited`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}},"patch":{"operationId":"updateWhatsAppCallingSettings","tags":["Llamadas de WhatsApp"],"summary":"Encender las llamadas y cambiar su horario","description":"Enciende o apaga las llamadas del número (`enabled`) y cambia el botón de llamar del chat (`call_icon_visible`), el permiso de devolución de llamada (`callback_permission`) y el horario (`call_hours` con `timezone_id`, `weekly` de hasta 2 tramos por día en HHMM y `holidays`; `null` lo quita). Lo que no mandes queda como está en Meta. Meta decide si el número es elegible (424 `meta_error` si no). Para recibir los eventos, la app de Meta tiene que tener suscrito el campo de webhook `calls`. Solo con una key `sk_live_`: una key de prueba responde 403 `test_key_not_allowed` (el número compartido del sandbox no es tuyo).","security":[{"bearerAuth":[]}],"x-heysteff-scopes":["calls:write"],"parameters":[{"schema":{"type":"string","format":"uuid","example":"6f1c2a8e-3b4d-4e5f-9a0b-1c2d3e4f5a6b"},"required":true,"description":"`channel_connection_id` del número de WhatsApp Cloud (GET /v1/numbers o la consola).","name":"id","in":"path"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"enabled":{"type":"boolean"},"call_icon_visible":{"type":"boolean"},"callback_permission":{"type":"boolean"},"call_hours":{"type":["object","null"],"properties":{"timezone_id":{"type":"string","minLength":1,"maxLength":64},"weekly":{"type":"array","items":{"type":"object","properties":{"day":{"type":"string","enum":["MONDAY","TUESDAY","WEDNESDAY","THURSDAY","FRIDAY","SATURDAY","SUNDAY"]},"open":{"type":"string","pattern":"^([01]\\d|2[0-3])[0-5]\\d$"},"close":{"type":"string","pattern":"^([01]\\d|2[0-3])[0-5]\\d$"}},"required":["day","open","close"]},"minItems":1,"maxItems":14},"holidays":{"type":"array","items":{"type":"object","properties":{"date":{"type":"string","pattern":"^\\d{4}-\\d{2}-\\d{2}$"},"start":{"type":"string","pattern":"^([01]\\d|2[0-3])[0-5]\\d$"},"end":{"type":"string","pattern":"^([01]\\d|2[0-3])[0-5]\\d$"}},"required":["date","start","end"]},"maxItems":20}},"required":["timezone_id","weekly"]}},"required":["enabled"]}}}},"responses":{"200":{"description":"Ajustes actualizados.","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/WhatsAppCallingSettings"}},"required":["data"]}}}},"400":{"description":"El cuerpo o los parámetros no pasan la validación (`validation_error`, con el detalle por campo en `details`) o el JSON es inválido (`invalid_json`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"Falta la API key, es inválida, venció o fue revocada (`unauthorized`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"La API key no tiene el scope requerido, la IP no está permitida o el workspace está deshabilitado.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"No hay un número de WhatsApp Cloud activo con ese `channel_connection_id` en este workspace (`not_found`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"424":{"description":"Meta rechazó la operación o no respondió (`meta_error`, con `details.meta_code`). Algunos ajustes exigen un número dedicado de WhatsApp Cloud (no en coexistencia) y que Meta lo habilite.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"429":{"description":"Se superó el límite de solicitudes de la API key (`rate_limited`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/v1/whatsapp-numbers/{id}":{"get":{"operationId":"getWhatsAppNumberStatus","tags":["Números de WhatsApp"],"summary":"Salud y verificación de un número","description":"Un número de WhatsApp Cloud del workspace (cualquiera, no solo los de heysteff), en vivo desde Meta: calidad (`quality_rating`), límite de mensajería, estado del nombre visible y verificación de la empresa con el link para verificarla (`verify_url`). Si una parte falla, el resto igual responde y el motivo queda en `errors`. Solo con una key `sk_live_`: una key de prueba responde 403 `test_key_not_allowed` (el número compartido del sandbox no es tuyo).","security":[{"bearerAuth":[]}],"x-heysteff-scopes":["numbers:read"],"parameters":[{"schema":{"type":"string","format":"uuid","example":"6f1c2a8e-3b4d-4e5f-9a0b-1c2d3e4f5a6b"},"required":true,"description":"`channel_connection_id` del número de WhatsApp Cloud (GET /v1/numbers o la consola).","name":"id","in":"path"}],"responses":{"200":{"description":"Estado del número.","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/WhatsAppNumberStatus"}},"required":["data"]}}}},"400":{"description":"El id no es un UUID (`invalid_id`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"Falta la API key, es inválida, venció o fue revocada (`unauthorized`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"La API key no tiene el scope requerido, la IP no está permitida o el workspace está deshabilitado.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"No hay un número de WhatsApp Cloud activo con ese `channel_connection_id` en este workspace (`not_found`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"429":{"description":"Se superó el límite de solicitudes de la API key (`rate_limited`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/v1/whatsapp-numbers/{id}/business-profile":{"get":{"operationId":"getWhatsAppBusinessProfile","tags":["Números de WhatsApp"],"summary":"Ver el perfil de empresa","description":"El perfil de empresa que ven tus clientes en WhatsApp: info, descripción, dirección, correo, sitios web, rubro y foto. Solo con una key `sk_live_`: una key de prueba responde 403 `test_key_not_allowed` (el número compartido del sandbox no es tuyo).","security":[{"bearerAuth":[]}],"x-heysteff-scopes":["numbers:read"],"parameters":[{"schema":{"type":"string","format":"uuid","example":"6f1c2a8e-3b4d-4e5f-9a0b-1c2d3e4f5a6b"},"required":true,"description":"`channel_connection_id` del número de WhatsApp Cloud (GET /v1/numbers o la consola).","name":"id","in":"path"}],"responses":{"200":{"description":"Perfil.","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/WhatsAppBusinessProfile"}},"required":["data"]}}}},"400":{"description":"El id no es un UUID (`invalid_id`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"Falta la API key, es inválida, venció o fue revocada (`unauthorized`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"La API key no tiene el scope requerido, la IP no está permitida o el workspace está deshabilitado.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"No hay un número de WhatsApp Cloud activo con ese `channel_connection_id` en este workspace (`not_found`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"424":{"description":"Meta rechazó la operación o no respondió (`meta_error`, con `details.meta_code`). Algunos ajustes exigen un número dedicado de WhatsApp Cloud (no en coexistencia) y que Meta lo habilite.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"429":{"description":"Se superó el límite de solicitudes de la API key (`rate_limited`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}},"patch":{"operationId":"updateWhatsAppBusinessProfile","tags":["Números de WhatsApp"],"summary":"Cambiar el perfil de empresa","description":"Cambia los campos que mandes (al menos uno). La foto va en `profile_picture_url`: una URL https pública de un JPG o PNG (cuadrado, 192×192 px o más, hasta 5 MB) o la `url` de POST /v1/media; heysteff la descarga y la sube a Meta. Responde el perfil actualizado. El nombre visible no se cambia aquí: POST /v1/numbers/{id}/display-name (números de heysteff) o WhatsApp Manager. Solo con una key `sk_live_`: una key de prueba responde 403 `test_key_not_allowed` (el número compartido del sandbox no es tuyo).","security":[{"bearerAuth":[]}],"x-heysteff-scopes":["numbers:write"],"parameters":[{"schema":{"type":"string","format":"uuid","example":"6f1c2a8e-3b4d-4e5f-9a0b-1c2d3e4f5a6b"},"required":true,"description":"`channel_connection_id` del número de WhatsApp Cloud (GET /v1/numbers o la consola).","name":"id","in":"path"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"about":{"type":"string","minLength":1,"maxLength":139},"address":{"type":"string","maxLength":256},"description":{"type":"string","maxLength":512},"email":{"anyOf":[{"type":"string","maxLength":128,"format":"email"},{"type":"string","enum":[""]}]},"websites":{"type":"array","items":{"type":"string","maxLength":256,"format":"uri"},"maxItems":2},"vertical":{"type":"string","enum":["UNDEFINED","OTHER","AUTO","BEAUTY","APPAREL","EDU","ENTERTAIN","EVENT_PLAN","FINANCE","GROCERY","GOVT","HOTEL","HEALTH","NONPROFIT","PROF_SERVICES","RETAIL","TRAVEL","RESTAURANT","ALCOHOL","ONLINE_GAMBLING","PHYSICAL_GAMBLING","OTC_DRUGS","NOT_A_BIZ"]},"profile_picture_url":{"type":"string","maxLength":2000,"format":"uri"}},"additionalProperties":false}}}},"responses":{"200":{"description":"Perfil actualizado.","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/WhatsAppBusinessProfile"}},"required":["data"]}}}},"400":{"description":"El cuerpo o los parámetros no pasan la validación (`validation_error`, con el detalle por campo en `details`) o el JSON es inválido (`invalid_json`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"Falta la API key, es inválida, venció o fue revocada (`unauthorized`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"La API key no tiene el scope requerido, la IP no está permitida o el workspace está deshabilitado.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"No hay un número de WhatsApp Cloud activo con ese `channel_connection_id` en este workspace (`not_found`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"413":{"description":"La foto pesa más de 5 MB (`file_too_large`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"422":{"description":"La foto no es un JPG o PNG (`invalid_profile_picture`), no se pudo descargar (`media_fetch_failed`) o la `url` de POST /v1/media no es de este workspace o ya no existe (`invalid_media`, `media_not_found`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"424":{"description":"Meta rechazó la operación o no respondió (`meta_error`, con `details.meta_code`). Algunos ajustes exigen un número dedicado de WhatsApp Cloud (no en coexistencia) y que Meta lo habilite.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"429":{"description":"Se superó el límite de solicitudes de la API key (`rate_limited`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/v1/agent":{"get":{"operationId":"getAgent","tags":["Agente de IA"],"summary":"Ver el agente de IA","description":"Encendido, instrucciones, proveedor, modelo, temperatura, keys propias (solo los últimos 4), cupo de prueba del mes, interruptores por canal y por cliente y los modelos sugeridos con su precio.","security":[{"bearerAuth":[]}],"x-heysteff-scopes":["agent:read"],"responses":{"200":{"description":"Agente.","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/Agent"}},"required":["data"]}}}},"401":{"description":"Falta la API key, es inválida, venció o fue revocada (`unauthorized`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"La API key no tiene el scope requerido, la IP no está permitida o el workspace está deshabilitado.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"429":{"description":"Se superó el límite de solicitudes de la API key (`rate_limited`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}},"patch":{"operationId":"updateAgent","tags":["Agente de IA"],"summary":"Configurar el agente de IA","description":"Todos los campos son opcionales (al menos uno); `provider` va siempre con `model` (422 `model_required`). `channels` y `customers` encienden (`true`), apagan (`false`) o vuelven a heredar (`null`) el agente en un canal o en todos los canales de un cliente. Las respuestas se pagan con tu key del proveedor si la guardaste (PUT /v1/agent/keys); si no, descuentan del cupo de prueba del plan (Developer Free 200, Developer 2.000, Developer Platform 20.000 al mes). Sin key ni cupo el agente no responde y llega el webhook `agent.failed` con `ai_quota_exhausted`: nunca otro proveedor.","security":[{"bearerAuth":[]}],"x-heysteff-scopes":["agent:write"],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"enabled":{"type":"boolean"},"instructions":{"type":"string","minLength":1,"maxLength":10000},"provider":{"type":"string","enum":["anthropic","openai","deepseek","openrouter"]},"model":{"type":"string","minLength":1,"maxLength":200,"pattern":"^[A-Za-z0-9._:/-]+$"},"temperature":{"type":"number","minimum":0,"maximum":1},"channels":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"enabled":{"type":["boolean","null"]}},"required":["id","enabled"],"additionalProperties":false},"maxItems":100},"customers":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"enabled":{"type":["boolean","null"]}},"required":["id","enabled"],"additionalProperties":false},"maxItems":100}},"additionalProperties":false}}}},"responses":{"200":{"description":"Agente actualizado.","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/Agent"}},"required":["data"]}}}},"400":{"description":"El cuerpo o los parámetros no pasan la validación (`validation_error`, con el detalle por campo en `details`) o el JSON es inválido (`invalid_json`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"Falta la API key, es inválida, venció o fue revocada (`unauthorized`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"La API key no tiene el scope requerido, la IP no está permitida o el workspace está deshabilitado.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"Un canal o cliente de la lista no existe (`channel_not_found`, `customer_not_found`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"429":{"description":"Se superó el límite de solicitudes de la API key (`rate_limited`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/v1/agent/keys":{"put":{"operationId":"setAgentKey","tags":["Agente de IA"],"summary":"Guardar tu key del proveedor (BYOK)","description":"Guarda cifrada (o reemplaza) tu key de Anthropic, OpenAI, DeepSeek u OpenRouter. Nunca se devuelve: la respuesta trae el proveedor y los últimos 4.","security":[{"bearerAuth":[]}],"x-heysteff-scopes":["agent:write"],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"provider":{"type":"string","enum":["anthropic","openai","deepseek","openrouter"]},"api_key":{"type":"string","minLength":20,"maxLength":500,"pattern":"^\\S+$"}},"required":["provider","api_key"],"additionalProperties":false}}}},"responses":{"200":{"description":"Key guardada.","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"provider":{"type":"string"},"last4":{"type":"string"},"updated_at":{"type":"string"}},"required":["provider","last4","updated_at"]}},"required":["data"]}}}},"400":{"description":"El cuerpo o los parámetros no pasan la validación (`validation_error`, con el detalle por campo en `details`) o el JSON es inválido (`invalid_json`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"Falta la API key, es inválida, venció o fue revocada (`unauthorized`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"La API key no tiene el scope requerido, la IP no está permitida o el workspace está deshabilitado.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"429":{"description":"Se superó el límite de solicitudes de la API key (`rate_limited`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/v1/agent/keys/{provider}":{"delete":{"operationId":"deleteAgentKey","tags":["Agente de IA"],"summary":"Borrar tu key del proveedor","security":[{"bearerAuth":[]}],"x-heysteff-scopes":["agent:write"],"parameters":[{"schema":{"type":"string","enum":["anthropic","openai","deepseek","openrouter"]},"required":true,"name":"provider","in":"path"}],"responses":{"200":{"description":"Resultado.","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"deleted":{"type":"boolean"},"provider":{"type":"string"}},"required":["deleted","provider"]}},"required":["data"]}}}},"400":{"description":"El cuerpo o los parámetros no pasan la validación (`validation_error`, con el detalle por campo en `details`) o el JSON es inválido (`invalid_json`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"Falta la API key, es inválida, venció o fue revocada (`unauthorized`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"La API key no tiene el scope requerido, la IP no está permitida o el workspace está deshabilitado.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"429":{"description":"Se superó el límite de solicitudes de la API key (`rate_limited`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/v1/agent/tools":{"get":{"operationId":"listAgentTools","tags":["Agente de IA"],"summary":"Listar custom tools","description":"Las llamadas HTTP a tu backend que el agente puede hacer durante una conversación.","security":[{"bearerAuth":[]}],"x-heysteff-scopes":["agent:read"],"responses":{"200":{"description":"Custom tools.","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/AgentTool"}}},"required":["data"]}}}},"401":{"description":"Falta la API key, es inválida, venció o fue revocada (`unauthorized`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"La API key no tiene el scope requerido, la IP no está permitida o el workspace está deshabilitado.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"429":{"description":"Se superó el límite de solicitudes de la API key (`rate_limited`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}},"post":{"operationId":"createAgentTool","tags":["Agente de IA"],"summary":"Crear una custom tool","description":"La auth hacia tu backend (bearer, HMAC) se configura en la consola: por la API no viajan secretos de terceros. Cupo del plan: Developer Free 2, Developer 10, Developer Platform ilimitadas.","security":[{"bearerAuth":[]}],"x-heysteff-scopes":["agent:write"],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"slug":{"type":"string","minLength":1,"maxLength":80,"pattern":"^[a-z0-9_-]+$"},"name":{"type":"string","minLength":1,"maxLength":200},"description":{"type":"string","minLength":1,"maxLength":2000},"mode":{"type":"string","enum":["simple","advanced"],"default":"simple"},"spec":{"type":"object","properties":{"method":{"type":"string","enum":["GET","POST","PUT","PATCH","DELETE"]},"url_template":{"type":"string","format":"uri"},"headers":{"type":"object","additionalProperties":{"type":"string"}},"query_template":{"type":"object","additionalProperties":{"type":"string"}},"body_template":{},"parameters_schema":{"type":"object","properties":{"type":{"type":"string","enum":["object"]},"properties":{"type":"object","additionalProperties":{}},"required":{"type":"array","items":{"type":"string"}},"additionalProperties":{"type":"boolean"}},"required":["type","properties"]},"response_path":{"type":"string"},"timeout_ms":{"type":"integer","minimum":500,"maximum":20000},"allow_hosts":{"type":"array","items":{"type":"string"}}},"required":["method","url_template","parameters_schema"]},"auth_kind":{"type":"string","enum":["none","bearer","header","basic"],"default":"none"},"product_catalog":{"type":"boolean","default":false}},"required":["slug","name","description","spec"]}}}},"responses":{"201":{"description":"Custom tool creada.","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/AgentTool"}},"required":["data"]}}}},"400":{"description":"El cuerpo o los parámetros no pasan la validación (`validation_error`, con el detalle por campo en `details`) o el JSON es inválido (`invalid_json`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"Falta la API key, es inválida, venció o fue revocada (`unauthorized`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"402":{"description":"Se alcanzó un límite del plan (`plan_limit_exceeded`, con `upgrade_url` en `details`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"La API key no tiene el scope requerido, la IP no está permitida o el workspace está deshabilitado.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"429":{"description":"Se superó el límite de solicitudes de la API key (`rate_limited`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/v1/agent/tools/{id}":{"get":{"operationId":"getAgentTool","tags":["Agente de IA"],"summary":"Ver una custom tool","security":[{"bearerAuth":[]}],"x-heysteff-scopes":["agent:read"],"parameters":[{"schema":{"type":"string","format":"uuid","example":"6f1c2a8e-3b4d-4e5f-9a0b-1c2d3e4f5a6b"},"required":true,"name":"id","in":"path"}],"responses":{"200":{"description":"Custom tool.","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/AgentTool"}},"required":["data"]}}}},"400":{"description":"El cuerpo o los parámetros no pasan la validación (`validation_error`, con el detalle por campo en `details`) o el JSON es inválido (`invalid_json`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"Falta la API key, es inválida, venció o fue revocada (`unauthorized`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"La API key no tiene el scope requerido, la IP no está permitida o el workspace está deshabilitado.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"La custom tool no existe en este workspace (`tool_not_found`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"429":{"description":"Se superó el límite de solicitudes de la API key (`rate_limited`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}},"patch":{"operationId":"updateAgentTool","tags":["Agente de IA"],"summary":"Editar una custom tool","description":"`spec` se reemplaza completa: manda la spec entera con tu cambio.","security":[{"bearerAuth":[]}],"x-heysteff-scopes":["agent:write"],"parameters":[{"schema":{"type":"string","format":"uuid","example":"6f1c2a8e-3b4d-4e5f-9a0b-1c2d3e4f5a6b"},"required":true,"name":"id","in":"path"}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string","minLength":1,"maxLength":200},"description":{"type":"string","minLength":1,"maxLength":2000},"mode":{"type":"string","enum":["simple","advanced"],"default":"simple"},"spec":{"type":"object","properties":{"method":{"type":"string","enum":["GET","POST","PUT","PATCH","DELETE"]},"url_template":{"type":"string","format":"uri"},"headers":{"type":"object","additionalProperties":{"type":"string"}},"query_template":{"type":"object","additionalProperties":{"type":"string"}},"body_template":{},"parameters_schema":{"type":"object","properties":{"type":{"type":"string","enum":["object"]},"properties":{"type":"object","additionalProperties":{}},"required":{"type":"array","items":{"type":"string"}},"additionalProperties":{"type":"boolean"}},"required":["type","properties"]},"response_path":{"type":"string"},"timeout_ms":{"type":"integer","minimum":500,"maximum":20000},"allow_hosts":{"type":"array","items":{"type":"string"}}},"required":["method","url_template","parameters_schema"]},"auth_kind":{"type":"string","enum":["none","bearer","header","basic"],"default":"none"},"product_catalog":{"type":"boolean","default":false},"is_active":{"type":"boolean"}}}}}},"responses":{"200":{"description":"Custom tool actualizada.","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/AgentTool"}},"required":["data"]}}}},"400":{"description":"El cuerpo o los parámetros no pasan la validación (`validation_error`, con el detalle por campo en `details`) o el JSON es inválido (`invalid_json`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"Falta la API key, es inválida, venció o fue revocada (`unauthorized`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"La API key no tiene el scope requerido, la IP no está permitida o el workspace está deshabilitado.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"La custom tool no existe en este workspace (`tool_not_found`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"429":{"description":"Se superó el límite de solicitudes de la API key (`rate_limited`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}},"delete":{"operationId":"deleteAgentTool","tags":["Agente de IA"],"summary":"Borrar una custom tool","security":[{"bearerAuth":[]}],"x-heysteff-scopes":["agent:write"],"parameters":[{"schema":{"type":"string","format":"uuid","example":"6f1c2a8e-3b4d-4e5f-9a0b-1c2d3e4f5a6b"},"required":true,"name":"id","in":"path"}],"responses":{"200":{"description":"Borrada.","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"deleted":{"type":"boolean"}},"required":["id","deleted"]}},"required":["data"]}}}},"400":{"description":"El cuerpo o los parámetros no pasan la validación (`validation_error`, con el detalle por campo en `details`) o el JSON es inválido (`invalid_json`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"Falta la API key, es inválida, venció o fue revocada (`unauthorized`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"La API key no tiene el scope requerido, la IP no está permitida o el workspace está deshabilitado.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"La custom tool no existe en este workspace (`tool_not_found`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"429":{"description":"Se superó el límite de solicitudes de la API key (`rate_limited`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/v1/insights":{"get":{"operationId":"getInsights","tags":["Agente de IA"],"summary":"Hallazgos de tus conversaciones","description":"De la analítica de conversaciones de los últimos `days` días (30 por defecto): clientes frustrados (`frustrated`), lo que el agente no supo responder o respondió mal (`agent_gaps`) y conversaciones sin resolver (`unresolved`). `included=false` si el plan no trae analítica (Developer Free). Cuando un análisis encuentra algo llega el webhook `insight.detected`.","security":[{"bearerAuth":[]}],"x-heysteff-scopes":["insights:read"],"parameters":[{"schema":{"type":"integer","minimum":1,"maximum":90,"description":"Días hacia atrás (1 a 90). Por defecto 30."},"required":false,"name":"days","in":"query"},{"schema":{"type":"integer","minimum":1,"maximum":50,"description":"Elementos por lista (1 a 50). Por defecto 10."},"required":false,"name":"limit","in":"query"}],"responses":{"200":{"description":"Hallazgos.","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"included":{"type":"boolean"},"enabled":{"type":"boolean"},"range":{"type":["object","null"],"properties":{"from":{"type":"string"},"to":{"type":"string"},"days":{"type":"integer"}},"required":["from","to","days"]},"frustrated":{"type":"object","properties":{"total":{"type":"integer"},"items":{"type":"array","items":{"type":"object","additionalProperties":{}}}},"required":["total","items"]},"agent_gaps":{"type":"object","properties":{"total":{"type":"integer"},"items":{"type":"array","items":{"type":"object","additionalProperties":{}}}},"required":["total","items"]},"unresolved":{"type":"object","properties":{"total":{"type":"integer"},"items":{"type":"array","items":{"type":"object","additionalProperties":{}}}},"required":["total","items"]}},"required":["included","enabled","range","frustrated","agent_gaps","unresolved"]}},"required":["data"]}}}},"400":{"description":"El cuerpo o los parámetros no pasan la validación (`validation_error`, con el detalle por campo en `details`) o el JSON es inválido (`invalid_json`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"Falta la API key, es inválida, venció o fue revocada (`unauthorized`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"La API key no tiene el scope requerido, la IP no está permitida o el workspace está deshabilitado.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"429":{"description":"Se superó el límite de solicitudes de la API key (`rate_limited`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/v1/inbox-embeds":{"get":{"operationId":"listInboxEmbeds","tags":["Bandeja embebible"],"summary":"Listar tokens de la bandeja embebible","description":"Los tokens del workspace con su estado. El token en sí nunca se lista.","security":[{"bearerAuth":[]}],"x-heysteff-scopes":["inbox:embed"],"responses":{"200":{"description":"Tokens.","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/InboxEmbed"}}},"required":["data"]}}}},"401":{"description":"Falta la API key, es inválida, venció o fue revocada (`unauthorized`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"La API key no tiene el scope requerido, la IP no está permitida o el workspace está deshabilitado.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"429":{"description":"Se superó el límite de solicitudes de la API key (`rate_limited`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}},"post":{"operationId":"createInboxEmbed","tags":["Bandeja embebible"],"summary":"Crear un token de la bandeja embebible","description":"Crea un token para meter la bandeja de heysteff (WhatsApp, Instagram, Messenger, SMS y Gmail) en tu producto con un iframe a `developers.heysteff.com/embed/inbox?token=…`: lista de conversaciones, hilo, responder (texto, archivos y plantillas de WhatsApp con la ventana cerrada), marcar leído y tomar o soltar el bot, en tiempo real. La vista nunca muestra nada fuera del alcance. Filtros por la URL del iframe: `status`, `search`, `unread`, `channel`. Una key de prueba no puede crear tokens.","security":[{"bearerAuth":[]}],"x-heysteff-scopes":["inbox:embed"],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string","minLength":1,"maxLength":120,"example":"Bandeja de Acme"},"scope":{"type":"string","enum":["all","channel","customer"],"description":"`all` (por defecto), `channel` o `customer`."},"channel_connection_id":{"type":"string","format":"uuid","description":"Con `scope=channel`."},"customer_id":{"type":"string","format":"uuid","description":"Con `scope=customer`: los canales que conectó ese cliente."},"allowed_origins":{"type":"array","items":{"type":"string"},"minItems":1,"maxItems":20,"description":"Quién puede enmarcar la vista (CSP `frame-ancestors`): `https://host[:puerto]`, con comodín de subdominio, o `http://localhost` para probar.","example":["https://app.acme.com","https://*.acme.com"]},"theme":{"type":"string","enum":["system","light","dark"]},"locale":{"type":"string","enum":["auto","es","en","pt"]},"expires_in_seconds":{"type":"integer","minimum":300,"maximum":31536000,"description":"Sin esto, no vence. Para un token por sesión de tu usuario, créalo desde tu backend con un vencimiento corto."}},"required":["name","allowed_origins"]}}}},"responses":{"201":{"description":"Token creado.","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/InboxEmbedCreated"}},"required":["data"]}}}},"400":{"description":"El cuerpo o los parámetros no pasan la validación (`validation_error`, con el detalle por campo en `details`) o el JSON es inválido (`invalid_json`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"Falta la API key, es inválida, venció o fue revocada (`unauthorized`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"La API key no tiene el scope requerido, la IP no está permitida o el workspace está deshabilitado.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"409":{"description":"El workspace llegó al tope de tokens activos (`inbox_embed_limit`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"422":{"description":"El canal o el cliente del alcance no existe (`channel_not_found`, `customer_not_found`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"429":{"description":"Se superó el límite de solicitudes de la API key (`rate_limited`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/v1/inbox-embeds/{id}":{"get":{"operationId":"getInboxEmbed","tags":["Bandeja embebible"],"summary":"Ver un token de la bandeja embebible","security":[{"bearerAuth":[]}],"x-heysteff-scopes":["inbox:embed"],"parameters":[{"schema":{"type":"string","format":"uuid","example":"6f1c2a8e-3b4d-4e5f-9a0b-1c2d3e4f5a6b"},"required":true,"name":"id","in":"path"}],"responses":{"200":{"description":"Token.","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/InboxEmbed"}},"required":["data"]}}}},"401":{"description":"Falta la API key, es inválida, venció o fue revocada (`unauthorized`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"La API key no tiene el scope requerido, la IP no está permitida o el workspace está deshabilitado.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"El token no existe en este workspace (`inbox_embed_not_found`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"429":{"description":"Se superó el límite de solicitudes de la API key (`rate_limited`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}},"patch":{"operationId":"updateInboxEmbed","tags":["Bandeja embebible"],"summary":"Editar un token de la bandeja embebible","description":"Cambia `name`, `allowed_origins`, `theme` o `locale`. El alcance no cambia: para otro alcance, crea otro token.","security":[{"bearerAuth":[]}],"x-heysteff-scopes":["inbox:embed"],"parameters":[{"schema":{"type":"string","format":"uuid","example":"6f1c2a8e-3b4d-4e5f-9a0b-1c2d3e4f5a6b"},"required":true,"name":"id","in":"path"}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string","minLength":1,"maxLength":120},"allowed_origins":{"type":"array","items":{"type":"string"},"minItems":1,"maxItems":20,"description":"Quién puede enmarcar la vista (CSP `frame-ancestors`): `https://host[:puerto]`, con comodín de subdominio, o `http://localhost` para probar.","example":["https://app.acme.com","https://*.acme.com"]},"theme":{"type":"string","enum":["system","light","dark"]},"locale":{"type":"string","enum":["auto","es","en","pt"]}}}}}},"responses":{"200":{"description":"Token editado.","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/InboxEmbed"}},"required":["data"]}}}},"400":{"description":"El cuerpo o los parámetros no pasan la validación (`validation_error`, con el detalle por campo en `details`) o el JSON es inválido (`invalid_json`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"Falta la API key, es inválida, venció o fue revocada (`unauthorized`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"La API key no tiene el scope requerido, la IP no está permitida o el workspace está deshabilitado.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"El token no existe en este workspace (`inbox_embed_not_found`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"429":{"description":"Se superó el límite de solicitudes de la API key (`rate_limited`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}},"delete":{"operationId":"revokeInboxEmbed","tags":["Bandeja embebible"],"summary":"Revocar un token de la bandeja embebible","description":"La vista deja de cargar y su tiempo real se corta. Revocar dos veces no hace nada.","security":[{"bearerAuth":[]}],"x-heysteff-scopes":["inbox:embed"],"parameters":[{"schema":{"type":"string","format":"uuid","example":"6f1c2a8e-3b4d-4e5f-9a0b-1c2d3e4f5a6b"},"required":true,"name":"id","in":"path"}],"responses":{"200":{"description":"Token revocado.","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/InboxEmbed"}},"required":["data"]}}}},"401":{"description":"Falta la API key, es inválida, venció o fue revocada (`unauthorized`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"La API key no tiene el scope requerido, la IP no está permitida o el workspace está deshabilitado.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"El token no existe en este workspace (`inbox_embed_not_found`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"429":{"description":"Se superó el límite de solicitudes de la API key (`rate_limited`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/v1/logs":{"get":{"operationId":"listLogs","tags":["Logs"],"summary":"Logs unificados","description":"Llamadas a la API, entregas de webhooks y mensajes que el proveedor rechazó, en una sola línea de tiempo (de lo más nuevo a lo más viejo), con el conteo del período por fuente en `counts`. Es lo mismo que la página Logs de la consola. Las llamadas a la API se guardan 14 días.","security":[{"bearerAuth":[]}],"x-heysteff-scopes":["logs:read"],"parameters":[{"schema":{"type":"string","description":"Fuentes separadas por coma (por omisión todas): api, webhooks, messages.","example":"api,webhooks"},"required":false,"name":"sources","in":"query"},{"schema":{"type":"string","enum":["true","false"],"description":"Solo errores: llamadas 4xx/5xx, entregas sin 2xx y mensajes fallidos."},"required":false,"name":"errors_only","in":"query"},{"schema":{"type":"string","maxLength":200,"description":"Busca en ruta, método, evento, URL, canal, error o id."},"required":false,"name":"q","in":"query"},{"schema":{"type":"string","enum":["1h","24h","7d","14d"],"description":"Ventana hacia atrás desde ahora. Por omisión 24h. `since` manda sobre `range`."},"required":false,"name":"range","in":"query"},{"schema":{"type":"string","format":"date-time"},"required":false,"name":"since","in":"query"},{"schema":{"type":"string","format":"date-time"},"required":false,"name":"until","in":"query"},{"schema":{"type":"string","format":"date-time","description":"Página siguiente: `next_before` de la respuesta anterior."},"required":false,"name":"before","in":"query"},{"schema":{"type":"string","maxLength":80,"description":"Va con `before`: `next_before_id` de la respuesta anterior (desempata registros del mismo instante)."},"required":false,"name":"before_id","in":"query"},{"schema":{"type":"integer","minimum":1,"maximum":200},"required":false,"name":"limit","in":"query"}],"responses":{"200":{"description":"Página de logs.","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/LogEntry"}},"counts":{"type":"object","properties":{"api":{"type":"integer"},"webhooks":{"type":"integer"},"messages":{"type":"integer"}},"required":["api","webhooks","messages"]},"total":{"type":"integer"},"since":{"type":"string","format":"date-time"},"until":{"type":"string","format":"date-time"},"next_before":{"type":["string","null"]},"next_before_id":{"type":["string","null"]},"retention_days":{"type":"integer"}},"required":["data","counts","total","since","until","next_before","next_before_id","retention_days"]}}}},"400":{"description":"El cuerpo o los parámetros no pasan la validación (`validation_error`, con el detalle por campo en `details`) o el JSON es inválido (`invalid_json`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"Falta la API key, es inválida, venció o fue revocada (`unauthorized`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"La API key no tiene el scope requerido, la IP no está permitida o el workspace está deshabilitado.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"429":{"description":"Se superó el límite de solicitudes de la API key (`rate_limited`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}}},"webhooks":{}}