Autenticación y permisos
El formato de la clave, los permisos por dominio, qué está abierto a claves y los errores que devuelven el portero y la entrada de la API.
La clave
Cada llamada lleva la clave completa en la cabecera Authorization, tal como se mostró al crearla, incluido el <id>| del principio:
Authorization: Bearer <id>|fc_live_...- Una clave, una cuenta. La clave accede solo a los datos de la cuenta que la creó. No hay claves para varias cuentas. (D9)
- Sin OAuth. No hay intercambio de tokens ni refresco: la clave se manda tal cual en cada llamada. (D8)
- No vence. Guardala como una contraseña, fuera del código y de los repositorios. Si se filtra, revocala desde la pantalla Desarrolladores: según el contrato, deja de funcionar en todos los dominios desde la llamada siguiente. (C1)
- Lleva un
|. En un archivo.env, escribila entre comillas:FC_API_KEY='<id>|fc_live_…'. Sin comillas, si cargás el archivo consource, la shell toma el|como una tubería y la variable queda vacía. - Revocarla corta también los webhooks atados a ella. Una suscripción queda atada a la clave que la creó o a la última clave que le cambió la URL. Cuando esa clave se revoca, deja de recibir eventos en el acto y pasa a
status: disabledcondisabled_reason: "key_revoked"; no se puede volver a habilitar, así que hay que crear una nueva. Las suscripciones creadas desde el panel que ninguna clave redirigió son de la cuenta y no cambian. Ningún campo de la suscripción dice a qué clave está atada: llevá vos el registro de qué clave usó cada integración. (enmienda C3 del 04-10, E18, E19, E40) - Tope diario de llamadas: 10.000 por día por clave en accounts y en communities, cada área por su lado. Al superarlo,
429 daily_cap_exceeded. Si el tope se puede levantar a pedido es algo que el contrato anterior decía (D16, C6) y que el de la URL única no repite: pendiente de confirmar. (D16, C6; URL única, Q6; Q20 del 14-10) - conversations no tiene tope diario. Lo que se limita son los envíos a WhatsApp, por el número que manda y no por la clave: ver Ritmo por número. (Q20 del 14-10)
- Cada llamada queda registrada (clave, ruta, método, código de respuesta, hora y duración) para entender cómo se usa el piloto. No se guarda el contenido. (C2, D12)
Todas las claves son fc_live_: no hay claves de prueba ni sandbox. Lo que hagas con una clave actúa sobre datos y números reales. (C1, D17)
Permisos
Cada ruta pide un permiso y los permisos se eligen al crear la clave. Son de la forma dominio:grado: leer, escribir y, solo donde el dominio produce salida a WhatsApp, enviar u operar. (D10)
| Permiso | Qué habilita | Estado en v0 | Rutas en las instantáneas |
|---|---|---|---|
conversations:read | Leer contactos, conversaciones, mensajes, etiquetas, flujos, plantillas, tableros… | Abierto (etapa 1). | 75 |
conversations:write | Escribir sin enviar a WhatsApp: contactos, etiquetas, campos, tableros, notas, tickets, carpetas. | Abierto (etapa 1). La superficie amplificada sigue cerrada (route_not_available). | 69 |
conversations:send | Enviar mensajes 1:1 por WhatsApp. | Abierto, con las reglas de WhatsApp: ritmo por número, ventana de 24 horas y antibloqueo. | 8 |
communities:read | Consultar campañas, grupos, participantes, envíos y estadísticas. | Abierto. | 48 |
communities:write | Escritura sin egress a WhatsApp. | En v0 solo administra las suscripciones propias de communities, que no llegan por la URL base: las suscripciones se administran con webhooks:write. | 5 |
communities:send | Envíos a grupos que la cuenta ya administra. | Denegado a claves en v0. | 0 |
communities:operate | Acciones sobre la sesión de WhatsApp (sincronizar, importar, renovar link, reintentar). | Denegado a claves en v0. | 0 |
accounts:read | Leer la cuenta, sus números (devices), alertas y dominios propios. | Según la especificación de accounts. | 31 |
accounts:write | Modificar de forma reversible. | Según la especificación de accounts. | 25 |
accounts:operate | Las operaciones irreversibles de accounts (reset, limpieza, purga de cola y borrados). | Según la especificación de accounts; no figura en D10. | 5 |
webhooks:read | Leer las suscripciones de webhooks y sus entregas. | Abierto. Además, la clave necesita el permiso de lectura del área de cada evento de la suscripción. | 3 |
webhooks:write | Crear, modificar, probar y borrar suscripciones de webhooks. Incluye webhooks:read. | Abierto. Para elegir un evento, la clave necesita además el permiso de lectura de su área (conversations:read, communities:read o accounts:read). | 4 |
whalink:read | Leer links de wha-link. | wha-link no forma parte de este portal todavía. | — |
whalink:write | Escribir en wha-link. | wha-link no forma parte de este portal todavía. | — |
La última columna cuenta las operaciones de cada permiso en las instantáneas de hoy; en la referencia cada operación muestra el suyo.
Los nombres pueden cambiar
Los permisos de v0 usan los nombres de D10 y se van a unificar con los roles de workspaces (Q20). El contrato llama whalink al dominio de links; la pantalla de claves puede mostrarlo como wha-link.
Qué permisos tiene tu clave
En v0, una clave no puede consultar quién es ni qué permisos tiene. Es una limitación conocida:
GET /api/v1/me(accounts) pideaccounts:read. Con una clave sin ese permiso responde403 permission_denied, y con el permiso, según su especificación, devuelve los datos de la cuenta, no los permisos de la clave.- Las rutas de claves (
/api/v1/me/api-keys) responden403 route_not_availablea cualquier clave.
Qué hacer: guardá junto a la clave la lista de permisos que elegiste al crearla. Si una llamada responde 403 permission_denied, message dice cuál falta, por ejemplo: «Esta API key no tiene el permiso accounts:read.»
Qué está abierto a claves
La API se abre por radio de daño, no por dominio ni por ruta. (D21)
| Superficie | Qué incluye | Daño posible por llamada | Estado |
|---|---|---|---|
| A | Lectura y escritura sin envío a WhatsApp: contactos, etiquetas, campos, tableros, notas, tickets, listas, carpetas | Datos basura en la cuenta | Abierta (etapa 1) |
| B | Envío 1:1 | Un mensaje | Abierta, con el permiso conversations:send y las reglas de WhatsApp (ventana de 24 horas, frenos contra spam y ritmo por número) (Q19) |
| C | Envío amplificado: masivos, arrancar flujos, disparadores, etiqueta que dispara un flujo | N mensajes, sin tope en el pedido | Fuera del piloto |
El envío 1:1 pide conversations:send, un permiso aparte de conversations:write: elegilo al crear la clave solo si tu integración manda mensajes.
Ninguna clave habilita la superficie amplificada, donde una llamada produce N mensajes. En conversations son estas diez rutas, y responden 403 route_not_available con cualquier permiso: (C1)
broadcasts/{id}/executey sus dos reintentoscontacts/{contact}/start-flowflows/triggers/{uuid}/listencontacts/add-tagsycontacts/remove-tagscontacts/tags/bulk-operationcontacts-synccontacts/import/analyze
write no habilita envío, ni siquiera por adentro: agregarle una etiqueta a un contacto puede disparar un flujo que manda mensajes, y por eso esas rutas quedan afuera.
En communities, en v0, una clave lee: Escribir solo administra las suscripciones propias de communities, que no llegan por la URL base (las suscripciones se administran con webhooks:write), y Enviar y Operar no habilitan ninguna operación. La especificación de Communities (instantánea 6f32f0b4 (árbol con cambios sin commitear)) ya no dice que las claves estén pendientes. Confirmalo con una llamada real antes de depender de eso. (enmienda C1 del 04-10)
Webhooks: webhooks:read y webhooks:write administran las suscripciones de /api/v1/webhooks y no se implican con los de accounts. Para elegir un evento, la clave necesita además el permiso de lectura de su área; ver Webhooks.
Errores
Cuando el portero o la entrada de la API rechazan una llamada, el cuerpo trae error (un código estable) y message (para personas):
{
"error": "permission_denied",
"message": "Esta API key no tiene el permiso `conversations:send`."
}| Código | error | Qué pasó | Qué hacer |
|---|---|---|---|
401 | unauthenticated | Falta la clave, no existe o fue revocada. Según el contrato de la URL única es igual en las tres áreas. De las instantáneas de hoy, solo la de communities lo documenta, con la cabecera WWW-Authenticate: Bearer; las de conversations y accounts declaran para el 401 un cuerpo con solo message. Pendiente de confirmar en conversations y accounts. | Revisá la cabecera Authorization y que la clave siga activa. |
403 | permission_denied | La clave no tiene el permiso que pide la ruta: «Esta API key no tiene el permiso …». | Creá una clave con ese permiso. |
403 | route_not_available | La ruta no está disponible para claves en el piloto: superficie amplificada (por ejemplo contacts/add-tags) o una ruta que no figura en la referencia. | No hay permiso que pedir: ninguna clave la habilita. |
403 | — (solo message) | Lo rechazó la autorización propia del dominio, no el portero. | Leé message. |
404 | route_not_found | La ruta no existe en la API pública: se contesta sin llegar a ninguna área. | Revisá la ruta y que empiece con /api/v1/. |
422 | — | Error de validación: message y errors, con la lista de problemas por campo. | Corregí los campos indicados. |
429 | daily_cap_exceeded | Se superó el tope diario de llamadas de accounts o de communities (10.000 por día por clave en cada una). conversations no tiene tope diario. Trae Retry-After con los segundos que faltan para que se reinicie. | Esperá lo que indica Retry-After. |
429 | rate_limited | Un envío superó el ritmo del número por el que sale: 60 mensajes por segundo en un número oficial, 15 en coexistencia y sin límite por QR; además, 10 por minuto al mismo contacto en lo que sale por la Cloud API. Se cuenta por número, no por clave. No lo devuelve el portero sino las reglas de WhatsApp. Trae Retry-After. | Esperá lo que indica Retry-After y reintentá. |
429 | — | Límite por minuto de una ruta concreta. | Bajá el ritmo y reintentá. |
500 | edge_misconfigured | La entrada de la API no está configurada para llegar al área de la ruta y no reenvía la llamada. Es un problema de nuestro lado. | Reportalo citando el X-Request-Id. |
502 | origin_unreachable | La entrada de la API no pudo comunicarse con el área que atiende la ruta. | Reintentá con espera, cada vez más larga. Si sigue, reportalo citando el X-Request-Id. |
503 | gate_unavailable | El portero no pudo evaluar la llamada y, por seguridad, no la deja pasar. Trae Retry-After. | Reintentá en unos segundos. (C6 P7) |
503 gate_unavailable y 403 permission_denied son distintos a propósito: el primero es «no pude evaluar, reintentá»; el segundo, «evalué y no».
500 edge_misconfigured y 502 origin_unreachable los responde la entrada de la API, antes de llegar al área, con el mismo message: «La API no está disponible en este momento.». No traen Retry-After. Ante un 502 no hay garantía de que el área no haya recibido la llamada: si era una escritura, comprobá que no se aplicó antes de repetirla.
-
Cada respuesta trae
X-Request-Id, en éxito y en error, incluidos todos los de esta tabla. Es un UUID distinto para cada pedido. Al reportar un problema, citalo junto con la hora aproximada de la llamada: con ese id soporte encuentra la llamada. No se puede consultar por el id desde la API. Ver El id del pedido. (1019) -
Decidí por el código HTTP y por
error, no pormessage.messagees para personas: su texto y su idioma cambian según el dominio. -
Esperá un
403en cualquier ruta. Las especificaciones de los dominios no lo documentan en todas, y donde lo documentan el esquema trae solomessage, sinerror(conocido en v0).
Armá tu propio flujo
Un flujo completo con tus tiempos y tu lógica: te llega un mensaje, esperás, decidís, respondés con texto o con una plantilla y te enterás si lo leyeron.
Mensajes
Todo lo que podés mandar y recibir en una conversación 1:1, qué responde cada envío y cómo saber qué puede hacer cada número.