Changelog · página 2 de 9

Novedades de la API

Cada cambio de la superficie pública queda registrado aquí, del más nuevo al más viejo. Esta página cubre del 20 ago 2026 – 27 ago 2026. También puedes suscribirte al feed.

v2.46.0 Mejora

Los nombres de las operaciones ya no se pueden mover sin avisarte

Si generas un cliente a partir de nuestro contrato —o si usas el SDK de TypeScript, que ahora saca sus tipos de ahí— hay un cambio nuestro que te rompía el código sin que ninguno de los dos se enterara: renombrar una operación, o moverla de una ruta a otra.

Desde hoy no puede pasar en silencio. Los nombres y las rutas de las 318 operaciones publicadas están congelados, y un guardián frena cualquier cambio que los mueva antes de que salga.

Retirar una operación sigue siendo posible, pero solo por el camino de siempre: primero se deprecia —te llega en las cabeceras Deprecation y Sunset, y aquí en el changelog—, conviven al menos seis meses, y recién después se va.

Esto no cambia nada de lo que ya escribiste. Es una promesa sobre lo que no te vamos a hacer.

v2.43.0 Mejora

El SDK de TypeScript ya dice qué devuelve cada método

Hasta ahora, 165 de los 319 métodos del SDK de TypeScript devolvían unknown. El editor no te autocompletaba nada y tenías que adivinar la forma de cada respuesta —o dispararla contra la API para mirarla— antes de escribir el código que la usa.

Ya no: todos los métodos declaran su tipo, y esos tipos se generan desde el contrato público, así que no pueden desincronizarse de lo que la API responde de verdad.

const pagina = await met.events.list({ limit: 50 });
//    ^ el editor ya sabe: { object, data: EventEnvelopeDto[], has_more }

for (const ev of pagina.data) {
  ev.type;     // autocompleta los tipos del catálogo
  ev.created;  // number
}

Los tipos viajan dentro del paquete. Si usas TypeScript no tienes que hacer nada: actualiza y el editor empieza a ayudarte.

Dos cosas más que entraron con esto:

  • POST /conversions y POST /conversions/web ahora documentan su respuesta. Eran las dos únicas operaciones públicas que no lo hacían, y justo ahí importa: la compuerta puede descartar tu conversión y responder con éxito igual. Ahora el campo decision está documentado y sabes si llegó a Meta o se quedó en el camino.
  • Algunos tipos escritos a mano estaban equivocados, no solo ausentes: había métodos declarados como lista que en realidad devuelven un objeto. Generarlos desde el contrato los corrigió.
v2.42.1 Nuevo

Los leads del sitio llegan directamente al CRM interno

El formulario de demostración de Meteor ya no depende del CRM externo heredado. Ahora registra cada solicitud directamente en el workspace autorizado mediante:

curl -X POST https://api.met.meteor.com.co/api/v1/contacts/web-leads \
  -H "Authorization: Bearer $MET_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"name":"Ana Pérez","phone":"+573001234567","email":"ana@example.com"}'

El endpoint requiere el scope contacts:write, deduplica por teléfono y conserva la trazabilidad del origen con las etiquetas s.a.p y website-demo.

v2.42.0 Nuevo

Los eventos quedan guardados 30 días, los escuche alguien o no

Hasta ahora un evento existía solo en el instante en que se emitía: si tu servidor estaba caído, o si todavía no habías creado el webhook, ese evento no volvía nunca. Por eso conectar algo crítico a los eventos era una apuesta.

Ahora todo evento queda guardado 30 días, haya o no un endpoint escuchándolo, y se puede consultar después:

curl https://api.met.meteor.com.co/api/v1/events \
  -H "Authorization: Bearer $MET_API_KEY"
curl https://api.met.meteor.com.co/api/v1/events/evt_01M1065RWR7BXJAEE10Y605SMV \
  -H "Authorization: Bearer $MET_API_KEY"

Es el mismo sobre que se firma y se entrega por webhook, con el mismo id: si recibiste una entrega y quieres volver a leerla, pídela con ese id.

GET /events pagina por cursor como el resto de las listas: limit (hasta 100), starting_after con el id del último evento que viste, y has_more para saber si quedan más viejos. Con types filtras por tipo, separando con comas.

Solo ves los eventos cuyo permiso de lectura lleva tu clave: una clave sin billing:read no ve billing.threshold, aunque tenga events:read. Y una clave de prueba solo ve eventos de prueba. Ambos endpoints piden events:read.

v2.41.0 Mejora

Las Funciones IA pueden exigir validaciones previas

Las Funciones IA aceptan ahora required_tools, una lista de herramientas que deben terminar correctamente antes de ejecutar la función durante el turno actual.

El Met recibe el prerrequisito dentro de la descripción de la herramienta y el servidor también lo aplica. Si intenta ejecutar ambas herramientas en paralelo, o si la validación falla, la función dependiente no corre y devuelve un error reintentable required_tool_missing con la lista pendiente.

Esto permite proteger acciones transaccionales, por ejemplo exigir una consulta de inventario antes de registrar un pedido. Las funciones existentes conservan el comportamiento anterior porque required_tools es una lista vacía por defecto.

v2.40.0 Mejora

El límite por minuto sube a 300, y el 429 por fin trae el código que esta guía documenta

Dos cambios en el límite de requests por minuto. El segundo importa más que el primero.

El techo por defecto pasa de 100 a 300 por minuto

No hay que hacer nada: si tu key no tiene un límite propio asignado, ya tiene el nuevo. Las cabeceras X-RateLimit-* de cada respuesta reflejan el valor vigente, así que no hace falta que confíes en este número: míralo ahí.

El 300 sale de medir una semana de tráfico real, no de redondear hacia arriba. La integración más grande que tenemos nunca tocó el techo: su pico es de 32 requests por minuto. La que sí lo tocaba es un proceso por lotes que corre en ráfagas y pide unos 105 por minuto sostenidos mientras corre — perdía exactamente cinco por minuto contra un techo de 100. Su pico de llamadas atendidas fue 117. El nuevo valor es tres veces esa demanda real.

Si necesitas más de 300, no hace falta esperar a que cambiemos este número: se le puede asignar un límite propio a tu key.

El 429 ahora dice rate_limited

Esta guía siempre dijo que reintentes cuando el código sea rate_limited. El servidor respondía too_many_requests, con el mensaje ThrottlerException: Too Many Requests — el nombre de una clase interna, en inglés.

Si escribiste tu reintento siguiendo esta guía, esa rama de tu código nunca se ejecutó. Ahora sí:

{
  "success": false,
  "error": {
    "type": "rate_limit_error",
    "code": "rate_limited",
    "message": "Se superó el límite de requests por minuto de esta API key. Espera 12 segundos y reintenta (ver la cabecera Retry-After)."
  }
}

La cabecera Retry-After siempre estuvo ahí y sigue igual. Si usas nuestros SDK de TypeScript o Python no tienes que hacer nada: reintentan por el estado HTTP 429, no por el código del cuerpo, así que este cambio no los toca.

Si tu integración es a mano y llegaste a depender de too_many_requests —el valor que no estaba documentado— ese es el único caso que hay que ajustar.

Por qué esto importa más que el techo

En una sola ventana de un minuto vimos 894 rechazos contra una misma key. Eso no es demanda: es una integración que recibió un 429 y reintentó de inmediato, una y otra vez, construyendo su propia avalancha. Subir el techo no arregla ese patrón — solo mueve la pared. Lo que lo arregla es que el reintento con espera que ya escribiste, siguiendo esta guía, por fin se dispare.

v2.39.0 Mejora

El listado de contactos deja de cargar con dos campos de configuración interna

GET /contacts devolvía 573 KB por página de 100 contactos, y el 80,7% de ese peso eran dos claves que el listado no usa:

clave% de la respuesta
$followup_rules60,5%
$ad_referral20,2%

Las dos salen del listado. El detalle no cambia: GET /contacts/{id} las sigue devolviendo completas.

Por qué pesaban tanto

$followup_rules es la configuración de seguimientos que un flujo sincroniza en cada contacto para poder revalidarla cuando dispara. En el workspace que medimos, los 194 contactos tenían exactamente la misma versión — unos 3,5 KB repetidos fila por fila. Un listado de 100 contactos publicaba esa misma configuración cien veces.

$ad_referral es el texto completo del anuncio del que llegó el contacto. Útil al abrir la conversación; no al listar.

Qué vas a notar

La respuesta pesa alrededor de una quinta parte de lo que pesaba. En la medición contra la colección más grande, la consulta bajó de ~510-710 ms a ~265 ms, que es el piso de la red: el listado ya no depende del peso de cada contacto.

Si leías alguno de los dos desde el listado

Pídelos en el detalle. Un barrido que necesite $followup_rules de muchos contactos ahora hace una llamada por contacto — es más trabajo, y es deliberado: esa configuración es del flujo, no del contacto, y sacarla del listado es lo que lo vuelve rápido para todos los demás.

El resto de los campos no cambia. Los de identidad —$name, $phone, $email, $status, $tags— y todos tus campos personalizados siguen viajando igual.

De dónde salió esto

De medir el tráfico real de la API: GET /contacts era el segundo endpoint más usado de la plataforma y el más lento de los que se usan de verdad. Es el mismo criterio que ya se había aplicado en agosto a las respuestas de las herramientas del MCP, donde estas dos claves salieron por la misma razón.

v2.38.0 Mejora

La referencia de Partners deja de ser una lista de 37 filas

La API de Partners publica 37 operaciones y hasta hoy la referencia las mostraba casi todas al mismo nivel: GET /partner/clients y POST /partner/projects/{id}/milestones/{milestoneId}/complete una debajo de la otra, sin nada que dijera cuál era cuál.

Ahora quedan en siete grupos, cada uno con una línea que explica qué se puede hacer ahí y un enlace directo a su sección del portal:

GrupoOperacionesScope
Clientes1partner:clients:read
Comisiones1partner:commissions:read
Liquidaciones2partner:payouts:read
Oportunidades11partner:leads:*
Proyectos15partner:projects:*
Tickets6partner:support:*
MCP1mcp:use

Los grupos coinciden con los scopes

No es casualidad ni estética: lo que ves en un grupo es exactamente lo que una key de ese dominio puede hacer, ni más ni menos. Si tu key tiene partner:leads:*, el grupo «Oportunidades» es tu superficie completa.

Comisiones y Liquidaciones quedan separados aunque sumen tres operaciones entre los dos. Juntarlos por «son pocos» habría hecho que el grupo prometa algo que la key no alcanza.

El orden no es alfabético

Abre con las cuatro operaciones de solo lectura que contestan «¿cuánto llevo y de quién?» — es lo que se puede probar sin escribir nada. Después el pipeline, que son las 26 operaciones donde está toda la escritura. Después soporte. MCP cierra, porque es un endpoint JSON-RPC y no un recurso REST.

Y las dos referencias ahora se ven igual

El contrato de Met también declara sus grupos (x-tagGroups), así que la barra lateral se organiza igual en las dos referencias. Sus 27 grupos quedan repartidos en seis secciones: Empezar, Datos, CRM y canales, Automatización, Workspace y Partners.

Ninguna operación cambió de nombre, de ruta ni de forma. Si generas tu cliente del contrato, esto no te toca nada.

v2.37.0 Corrección

El contrato ya declara el sobre en el que viaja toda respuesta

Toda respuesta correcta de la API viaja dentro de un sobre: no llega el recurso pelado, llega envuelto en {"success": true, "data": …}. Siempre fue así. Lo que no era así es el contrato OpenAPI, que describía el recurso sin el sobre.

{ "success": true, "data": { "object": "identity", "livemode": true } }

La API no cambió. Ni un endpoint, ni un campo, ni un código de estado. Lo que cambió es el papel, que ahora dice lo que la API venía haciendo.

A quién le importa esto

Si usas los SDK oficiales de TypeScript o Python: a ti no. Te entregan el contenido de data y convierten el sobre de error en una excepción. No tienes que hacer nada.

Si generas tu cliente del contrato: regenéralo. El cliente que salía hasta hoy buscaba el recurso en la raíz de la respuesta; el que sale ahora lo busca en data, que es donde viaja. Con eso la forma cuadra con lo que la API responde.

Si ya habías parchado tu cliente generado a mano para leer .data, regenerar te va a devolver ese parche como redundante — bórralo.

Si llamas con curl o con un cliente propio: nada cambia, pero ahora la referencia te muestra la forma correcta en vez de la incompleta.

Lo que cambió, en números

334 de 353 operaciones públicas —298 de Met y 36 de la API de Partners— pasaron a declarar el sobre en sus respuestas 2xx. Las 19 restantes no lo llevan y tampoco lo declaran, que es lo correcto:

  • Seis envíos de WhatsApp (/channels/{id}/whatsapp/send-*) devuelven success en la raíz sin data. Su forma no cambió.
  • POST /mcp, en Met y en Partners, responde el sobre JSON-RPC que define la especificación de MCP, que es otro.
  • Las respuestas sin cuerpo JSON: los 204, el stream de eventos (text/event-stream) y la descarga de archivos.

La paginación va afuera de data

Una lista paginada pone pagination como hermana de data, no adentro:

{
  "success": true,
  "data": [ { "id": 9812 } ],
  "pagination": { "page": 1, "limit": 50, "total": 128, "pages": 3 }
}

Eso también era así antes y tampoco estaba declarado. Ahora sí.

Dónde leerlo

La guía de errores e idempotencia abre con una sección nueva sobre la forma de una respuesta, con el sobre de éxito y el de error uno al lado del otro.

v2.36.0 Corrección

La tabla de scopes decía menos de lo que la API aplica

La tabla de scopes de la guía de autenticación se mantenía a mano y se había quedado atrás del código. Ahora se genera del catálogo de scopes cruzado con el contrato OpenAPI, y hay un chequeo que falla si vuelve a separarse.

Lo que hay que revisar si usas keys de prueba

El párrafo de modo de prueba listaba cuatro scopes bloqueados. Son siete. Los tres que faltaban son justamente los que tienen efecto sobre dinero:

conversions:write        emite un evento real a Meta
workspaces:recharge      inicia un cobro real en Stripe
partner:billing:manage   deja una tarjeta en archivo

La API siempre los bloqueó —una key met_test_ recibe 403 con test_mode_restricted—, así que no cambió ningún comportamiento. Lo que estaba mal era la guía: decía que una llave de prueba llegaba a sitios a los que nunca llegó. Si diseñaste tu integración creyendo eso, revisa esa parte.

Seis scopes que la tabla no mencionaba

Faltaban conversions:read, conversions:write, rag:read, rag:write, workspaces:recharge y partner:billing:manage. Ya están, los 57.

La tabla ahora trae una columna Endpoints con cuántas operaciones piden cada scope, y distingue tres situaciones que antes se veían iguales:

  • Con endpoints: el número.
  • Sin endpoints pero con eventos detrás: conversions:read no lo pide ninguna operación REST, pero sin él no puedes suscribirte a conversion.sent ni a conversion.discarded. Antes la guía decía que no habilitaba nada.
  • Retirados del panel: rag:read, rag:write y sites:read ya no se ofrecen al emitir una key ni al registrar una app OAuth.

sites:read sale de la vitrina

Nunca tuvo endpoints detrás: marcarlo no habilitaba ninguna llamada. Deja de aparecer entre los scopes que puedes pedir.

Las keys que ya lo tengan guardado siguen funcionando igual. El scope sigue siendo válido y no invalida nada; simplemente no se ofrece de nuevo. Es el mismo criterio que se aplicó a rag:read y rag:write.

Guía nueva: Automatizaciones

Doce operaciones que estaban solo en la referencia ahora tienen narrativa: qué disparadores existen y qué condiciones exige cada uno, cómo se programa una recurrencia y en qué zona horaria corre, cómo se edita una automatización gestionada sin pisar un cambio ajeno (expected_revision), y por qué un run-now sobre un flujo devuelve un run que no aparece después en el listado.

Probar una habilidad ya no alcanza ítems ajenos

POST /workspaces/{id}/skills/{skillId}/test aceptaba cualquier identificador de ítem de la plataforma, y el nombre del ítem encontrado volvía dentro de la respuesta. Ahora la búsqueda queda acotada al catálogo de habilidades: un identificador que no sea de una habilidad responde «no existe», sin distinguir entre lo que no está y lo que no es tuyo.

v2.35.0 Nuevo

Ya puedes preguntar quién eres, y la lista de eventos dejó de ser prosa

Dos huecos del mismo tipo: cosas que la API sabía y no había forma de preguntarle. Ninguno cambia comportamiento existente.

GET /me — con qué identidad entra tu key

Antes no existía introspección. Para saber si una key servía había que llamar a algo real y deducir por el código de error: 401 la key está mal, 403 le falta el scope, 2xx anda. Eso responde "¿sirve?", nunca "¿quién soy?" — el workspace y el entorno salían de lo que tuvieras configurado de tu lado, no de lo que el servidor resolvió.

GET /api/v1/me
{
  "object": "identity",
  "livemode": true,
  "api_key": {
    "object": "api_key",
    "id": "key_01HXYZ",
    "owner_type": "workspace",
    "env": "live",
    "scopes": ["items:read", "runs:read"],
    "rate_limit_rpm": 120,
    "monthly_quota": null
  },
  "workspace": { "object": "workspace", "id": 7, "slug": "acme" },
  "partner": null
}

rate_limit_rpm y monthly_quota son los vigentes: el más estricto entre lo que dice tu key y lo que dice el plan del workspace. Es el número con el que te vas a encontrar en un 429, y descubrirlo a golpes es peor que leerlo.

El secreto de la key no sale, ni nada con qué reconstruirlo: ni prefijo, ni los últimos caracteres, ni la longitud. Lo único que la identifica es su id, el mismo que ves en el panel y el que queda en los registros de request — sirve para pegar en un ticket sin ser una credencial.

Hoy pide el scope runs:read. Es una limitación conocida y es la que menos quita: es exactamente el scope que había que probar antes para simular esto, así que ninguna key quedó con menos alcance del que ya tenía.

En los SDKs es met.me.retrieve(), en TypeScript y en Python.

met whoami dejó de adivinar

El comando ya no deduce: pregunta. Y como ahora el servidor le dice cuál es tu workspace, aparece un caso que antes era invisible — si tu configuración local apunta a un workspace y la key opera otro, te lo avisa en vez de imprimir el tuyo con un visto bueno. met whoami --save-workspace guarda el que resolvió el servidor.

Los 17 eventos de webhook, en el contrato

enabled_events documentaba su lista en un párrafo y validaba contra otra en el código. Ahora las dos salen de la misma constante: el catálogo de eventos es la fuente, el contrato publica el enum y el validador usa esos mismos valores. No pueden separarse.

Si generas tu cliente del contrato, enabled_events pasa a traer la lista cerrada de valores válidos —los 17 más *— en vez de un string libre.

v2.34.0 Cambio

El modelo de un Met se pide por tier, no por id

Al crear o actualizar un Met, el campo model ahora se documenta con los tres tiers y nada más:

PATCH /agents/{id}
{ "model": "brillante" }

Si hoy mandas un id de modelo, tu integración sigue funcionando. No hay 400 nuevo. Lo que cambia es qué queda guardado: un id se resuelve a su tier y el Met queda con el modelo vigente de ese tier, no con el que nombraste.

Por ejemplo, "claude-sonnet-4-5" deja un Met en el Brillante de hoy.

Por qué

Un id de modelo es nuestro, no tuyo. Fijar uno dejaba a ese Met afuera de cada mejora que hacemos —más barato, más rápido, mejor— en silencio y sin fecha de vencimiento. Nos pasó de verdad: dos Mets quedaron dos generaciones atrás mientras el resto de la flota avanzaba, y nadie se enteró hasta que fuimos a mirar.

Con el tier eliges cuánta cabeza quiere tu Met. Qué modelo la provee es problema nuestro, y es el tipo de problema que preferimos resolver sin pedirte que cambies código.

Qué hacer

Si guardas ids en tu configuración, cámbialos por agil, brillante o genio. La respuesta de un agente ya trae tier junto a model, así que puedes leer de ahí lo que necesites mostrar.

Los ids se seguirán aceptando durante la ventana de deprecación. Cuando cierre, avisaremos aquí antes de que empiecen a rechazarse.