Por qué caduca un token de Meta y qué pasa el día que caduca

Un token de acceso de Instagram conectado por Meta dura 60 días. PlanVortex lo renueva automáticamente un día antes de que caduque, así que normalmente no lo notas. Lo notas cuando esa renovación falla: la cuenta se marca como caída, deja de reintentarse sola y tu producto recibe un aviso para pedirle al usuario que la vuelva a conectar.
No todos los tokens de Meta funcionan así. El de una página de Facebook no tiene fecha de caducidad, el de un usuario del sistema dura lo que elijas al crearlo y WhatsApp tiene el suyo propio.
¿Cuánto dura un token de acceso de Meta?
Depende de cuál. Estos son los que se encuentra una integración:
| Token | Cuánto dura | Cómo se renueva |
|---|---|---|
| Token de usuario de Instagram (inicio de sesión de Instagram) | 60 días | ig_refresh_token, a partir de las 24 horas |
| Token de usuario de Facebook, de larga duración | Unos 60 días | Volviendo a pasar por el inicio de sesión |
| Token de página de Facebook, a partir de un token de usuario de larga duración | Sin fecha de caducidad | No hace falta renovarlo |
| Token de usuario del sistema (Business Manager) | 60 días o nunca, se elige al crearlo | fb_exchange_token con set_token_expires_in_60_days=true |
| Token de WhatsApp del panel de la app | Unas horas | Generando otro |
Para una cuenta de Instagram, 60 días es el tiempo que documenta Meta para el token de larga duración que se obtiene al conectarla, y es fijo: no se puede pedir uno que dure más ni hay un plan de pago que lo alargue.
// src/orm/social_apis/instagram/index.ts
const expiresDate = new Date();
expiresDate.setSeconds(expiresDate.getSeconds() + (60 * 60 * 24 * 60)); // 60 días
Ese expiresDate es lo único que decide si una cuenta necesita renovarse o no. No hay heurística ni margen de seguridad en ese cálculo: son 60 días exactos desde el instante en que Meta entregó el token, contados en segundos.
¿Por qué caduca el token de Instagram y el de una página no?
Porque representan cosas distintas. El de Instagram es un token de usuario: representa a la persona que autorizó la app, y Meta obliga a la integración a volver cada 60 días para demostrar que esa persona sigue detrás del permiso. La caducidad además limita el daño de un token que se filtra (deja de servir solo, sin que nadie tenga que revocarlo a mano).
El token de página que se genera a partir de un token de usuario de larga duración representa a la página, y Meta dice que no tiene fecha de caducidad y que solo se invalida «en determinadas condiciones». La guía de depuración de Meta enumera lo que invalida un token antes de tiempo: que la persona cierre sesión en la app, que cambie la contraseña o que revoque la autorización de la app. Por eso PlanVortex renueva las cuentas de Instagram en un ciclo y deja fuera las páginas de Facebook.
En los dos casos, quien integra la API tiene que tratar un token muerto como una parte normal del ciclo de vida de la cuenta, tanto si murió en una fecha como si algo lo invalidó.
¿Cómo se renueva un token antes de que caduque?
Con un job que corre una vez por hora y renueva cualquier cuenta cuyo token vaya a caducar en menos de 24 horas. La cadencia horaria existe precisamente para que sobre margen frente a esa ventana de un día:
// src/domain/repositories/account/index.ts: "Refrescamos con 1 dia de diferencia (requerido por instagram)"
const now = new Date();
now.setDate(new Date().getDate() + 1);
const account_match = {
deleted: false,
error_code: 0,
"access_data.expires_in": { $lte: now },
};
// src/jobs/index.ts
"refresh-tokens": {
cron: "7 * * * *", // cada hora, minuto 7
run: refreshTokens,
},
La renovación en sí es una llamada a refresh_access_token de Meta con grant_type=ig_refresh_token, que devuelve un token nuevo con otros 60 días por delante. Mientras esa llamada tenga éxito, el ciclo se repite solo y la cuenta no se entera de que su token cambió.
¿Cómo se consigue un token de Meta que no caduque?
Hay dos formas, y Meta está cerrando la segunda.
Un token de página. Se cambia el token de usuario de corta duración que devuelve el cuadro de inicio de sesión por uno de larga duración, y con ese se piden los tokens de las páginas:
GET /oauth/access_token?grant_type=fb_exchange_token&client_id={app-id}&client_secret={app-secret}&fb_exchange_token={token-de-usuario-corto}
GET /{user-id}/accounts?access_token={token-de-usuario-largo}
Cada página de la segunda respuesta trae su propio access_token, y ese no tiene fecha de caducidad. Dos condiciones: la persona que lo pide tiene que tener un rol en la página, y el token de usuario tiene que ser el de larga duración, que es el que pide Meta.
Un token de usuario del sistema. En el Business Manager (Configuración del negocio, luego Usuarios, luego Usuarios del sistema) se crea un usuario del sistema, se le asignan la app y los activos y se genera un token. Al generarlo se elige la caducidad: nunca, o 60 días. Meta recomienda el de 60 días como «una buena práctica de seguridad» y dice que «todas las integraciones deberían adoptar tokens con caducidad». A los negocios a los que ya se les aplica el requisito, pedir uno sin caducidad les devuelve un error. Así que el token de usuario del sistema permanente que recomiendan muchos tutoriales puede no estar a tu alcance.
Un token de usuario del sistema de 60 días se renueva con la misma llamada de intercambio y un parámetro más:
GET /oauth/access_token?grant_type=fb_exchange_token&client_id={app-id}&client_secret={app-secret}&set_token_expires_in_60_days=true&fb_exchange_token={token-actual}
La respuesta trae un token nuevo y su expires_in. Meta aconseja renovarlo dentro de los 60 días; si caduca, toca generar otro.
¿Qué token hay que usar con la API de WhatsApp Cloud?
El token de la pantalla de configuración de la API solo sirve para probar. El panel de la app da un token de usuario que, en palabras de Meta, caduca enseguida: habría que generar otro cada pocas horas, lo justo para mandar un primer mensaje de prueba.
Para lo que venga después, depende de quién sea el número:
- Para el número de tu propio negocio, un token de usuario del sistema, con todo lo dicho arriba sobre elegir 60 días o nunca.
- Para los números de tus clientes (Meta te llama proveedor de tecnología), un token de acceso de usuario del sistema de integración empresarial (Business Integration System User access token), uno por cliente, que se obtiene implementando el registro insertado (Embedded Signup) y canjeando el código que devuelve el flujo. Meta lo presenta como la forma de llegar a los activos de cada cliente sin pedirle que vuelva a autenticarse. Así conecta WhatsApp PlanVortex: el cliente pasa por el registro insertado y el código se canjea en el servidor.
¿Cómo se comprueba cuándo caduca un token de Meta?
El endpoint debug_token de Meta (el que hay detrás del depurador de tokens de acceso de sus herramientas para desarrolladores) devuelve, entre otros campos, expires_at y data_access_expires_at:
GET /debug_token?input_token={token-a-comprobar}&access_token={app-id}|{app-secret}
Son dos fechas distintas. expires_at es cuándo deja de funcionar el token. data_access_expires_at es la ventana de acceso a los datos, 90 días desde la última actividad de la persona: pasado ese plazo, la persona puede seguir iniciando sesión, pero la app pierde el acceso a sus datos hasta que vuelva a autorizar los permisos. PlanVortex llama a debug_token al conectar una cuenta de WhatsApp, para leer qué activos concedió la persona y cuándo caduca el token.
¿Duran lo mismo los tokens en todas las redes?
No: cada red fuerza su propio plazo, y un código de renovación que las trate a todas igual acaba dejando cuentas muertas.
| Red | Duración del access_token | Fuente |
|---|---|---|
| Instagram (Meta) | 60 días | instagram/index.ts |
| Página de Facebook (Meta) | Sin fecha de caducidad | fuera de getAccountsToRefresh |
| 60 días (5.184.000 s) | __tests__/social/contract/linkedin.test.ts |
|
| TikTok | 24 horas | __tests__/social/contract/youtube.test.ts |
| YouTube (Google) | 1 hora | youtube.types.d.ts |
YouTube es el caso extremo: con un token que dura una hora, el job horario no basta por sí solo, así que su SDK renueva además bajo demanda antes de cada llamada. Instagram y LinkedIn, con 60 días, viven cómodamente dentro de la pasada horaria con margen de sobra. Las páginas de Facebook están en el otro extremo: su token no tiene fecha, así que nunca entran en la pasada. La consecuencia práctica es que el código de renovación no puede ser genérico: cada SDK sabe cuánto dura su propio token y qué campo de la respuesta trae la nueva caducidad, porque ni siquiera se llaman igual entre redes.
¿Qué pasa el día que el token caduca de verdad?
Que la renovación automática falla, y a partir de ahí la cuenta deja de intentarse sola. Esto solo ocurre si algo impidió que el ciclo de 60 días se cerrase a tiempo (el usuario revocó el permiso desde Meta, la app perdió el caso de uso aprobado, o el job llevaba más de un día sin poder correr), nunca por el simple paso de los 60 días con el sistema funcionando con normalidad.
Cuando la llamada de renovación falla, pasan tres cosas a la vez:
- La cuenta se marca con un código de error (
error_code: 707, "Error while sincronyzing"). - Ese código la saca de la siguiente pasada del job: la consulta que selecciona cuentas a renovar exige
error_code: 0, así que una cuenta caída no se reintenta cada hora contra un token que ya no sirve. - Se encola un cambio de tipo
change_state_accountque se notifica por webhook, firmado, a las apps cliente que tengan ese cliente dado de alta.
El job procesa todas las cuentas que tocaba renovar en esa pasada en paralelo, agrupadas por cliente. Eso importa porque un cliente con varias cuentas de Instagram puede tener alguna que falle y el resto que se renueve sin problema en la misma pasada: el fallo de una no bloquea ni retrasa a las demás, y el webhook que le llega a la app cliente lleva agrupados los cambios de ese cliente, no un mensaje suelto por cada cuenta.
// src/jobs/RefreshTokens.ts
} catch (e) {
await AccountModel.updateOne({ _id: account._id }, { $set: { error_code: CustomError.ERROR_CODE_707.key } });
addAccountChange(grouped, account, {
field: ALLOWED_WEBHOOKS_NOTIFICATIONS.change_state_account,
id_account: account._id.toString(),
id_organization: account.id_organization._id.toString(),
social_network: account.social_network,
});
}
¿Cómo se entera tu producto de que una cuenta se desconectó?
Por un webhook, en cuanto el job detecta el fallo. Si tu app integra la API de PlanVortex, el webhook change_state_account te llega con id_account, id_organization y social_network, firmado igual que el resto de webhooks salientes de PlanVortex.
Enterarse por un error al publicar llega tarde: el usuario programó contenido para dentro de tres días dando por hecho que la cuenta seguía conectada, y el fallo no aparece hasta que ese contenido intenta salir. Con el webhook, tu producto puede avisar al usuario el mismo día que la cuenta se cayó, con tiempo de sobra para reconectarla antes de que nada se quede sin publicar.
¿Qué conviene tener resuelto si integras cuentas de Meta en tu producto?
Cuatro cosas, antes de dar por hecho que una cuenta conectada sigue conectada:
- Una cuenta desconectada es un estado, no un error. Pasa en el uso normal (el usuario revoca el permiso desde la configuración de Meta, cambia de contraseña, la app pierde acceso) y tu producto tiene que poder mostrarlo sin tratarlo como una excepción.
- Hay que escuchar el webhook, además del error de publicación. Es la diferencia entre avisar el día que la cuenta se cae y avisar el día que algo programado falla por su culpa. El código que lo recibe (con la verificación de firma sobre el cuerpo crudo, que es donde tropieza todo el mundo) está en las guías de Node.js y Python.
- La reconexión es siempre manual. Ni PlanVortex ni Meta pueden renovar un permiso que el usuario ha revocado; el flujo de conectar la cuenta es el mismo que la primera vez, código de autorización incluido.
- El mensaje al usuario no puede ser genérico, porque no todas las redes fallan igual. Una cuenta de TikTok caída y una de Instagram caída no comparten ni la causa más probable ni la urgencia: con un token de 24 horas, un fallo de TikTok casi siempre es un problema puntual del job; con uno de 60 días, un fallo de Instagram casi siempre significa que el usuario revocó el permiso.
¿Cuánto dura el token de acceso de una cuenta de Instagram conectada por Meta?
60 días desde que se conecta o se renueva. Es un tiempo fijo que fuerza Meta, no una elección de PlanVortex: el token de larga duración de una cuenta conectada con el inicio de sesión de Instagram caduca exactamente a los 60×60×24×60 segundos, y no hay forma de pedir uno que dure más. Se puede renovar en cuanto tiene al menos 24 horas, siempre que no haya caducado.
¿Hay algún token de acceso de Meta que no caduque nunca?
Sí, dos, y ninguno es el de Instagram. El token de página que se genera a partir de un token de usuario de larga duración no tiene fecha de caducidad, aunque Meta lo invalida si la persona cierra sesión, cambia la contraseña o revoca la app. El token de usuario del sistema del Business Manager se puede crear sin caducidad, pero Meta ahora recomienda el de 60 días y dice que todas las integraciones deberían adoptar tokens con caducidad.
¿Con cuánta antelación se renueva antes de caducar?
Con un día. El job de renovación corre cada hora y recoge cualquier cuenta cuyo token caduque en menos de 24 horas, así que en condiciones normales una cuenta nunca llega a los 60 días exactos sin haberse renovado ya.
¿Qué pasa si la renovación falla el día que el token caduca de verdad?
La cuenta se marca con un código de error y sale de la cola de renovación automática: sin eso, el job la reintentaría cada hora contra un token que ya no sirve. Tu producto recibe un webhook firmado avisando de que esa cuenta cambió de estado, y la app tiene que pedirle al usuario que vuelva a conectarla desde cero.
¿Todas las redes sociales caducan sus tokens igual que Meta?
No. LinkedIn también da 60 días, pero TikTok solo 24 horas y el access_token de Google (YouTube) dura una hora. Cada red fuerza su propio ritmo de renovación y PlanVortex lo respeta por separado en vez de tratarlos como si fueran iguales.