Changelog · página 4 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 31 jul 2026 – 5 ago 2026. También puedes suscribirte al feed.

v2.26.0 Mejora

La API de Partners ya documenta respuestas y errores de acceso reales

Las 37 operaciones de met.partner.* ahora describen su respuesta exitosa: clientes, comisiones, payouts, oportunidades, proyectos, hilos y tickets. El OpenAPI deja de mostrar respuestas vacías, así que la referencia y los clientes generados pueden conocer la forma curada de cada recurso.

También documentamos los errores que una integración puede recibir de verdad:

  • 403 cuando falta un scope, una capacidad no está habilitada o el recurso queda fuera de la cartera de la key.
  • 404 cuando un recurso no existe dentro de ese alcance, sin revelar recursos de otro tenant.
  • 409 en operaciones que mutan estado, incluidos los conflictos de Idempotency-Key.

Las requests autenticadas con API key devuelven ahora la taxonomía pública de error también cuando una validación o regla de dominio usa una excepción estándar: type, code, message y request_id. Las sesiones del panel conservan su formato anterior.

Cambio

Acceso API por plan mensual y nuevo plan Tech Partner

  • Las API keys se pueden crear y administrar desde el workspace antes de contratar un plan.
  • Para usar una key, el workspace operado debe tener un plan mensual vigente; también cuentan el trial y los planes bonificados.
  • Las keys de Partner validan el plan del workspace atribuido y ya no pueden operar un cliente sin suscripción activa.
  • Los límites de RPM y cuota mensual se resuelven desde el plan vigente al ejecutar cada key.
  • Tech Partner es un plan restringido de USD 0 de cargo fijo mensual, habilitado únicamente por Meteor para alianzas aprobadas; la Energía se cobra a tarifa premium.
Corrección

El CLI separa webhooks entrantes de entregas salientes

El comando met webhooks mezclaba dos direcciones distintas: lo que un tercero envía a Meteor y lo que Meteor entrega a tu servidor. Además, sample afirmaba enviar una prueba cuando solo leía el último payload entrante.

Ahora usa el grupo que corresponde al flujo:

# Tercero → Meteor: ver lo recibido, sin generar tráfico.
met inbound-webhooks events 12
met inbound-webhooks sample 12

# Meteor → tu servidor: probar, inspeccionar y reintentar entregas firmadas.
met webhook-endpoints test we_123
met webhook-endpoints deliveries we_123
met webhook-endpoints retry we_123 del_456

test y retry salen con código 0 solamente cuando tu servidor confirma la entrega. Si la API pudo iniciar el intento pero el receptor falla o vence el timeout, el CLI sale con 1, para que un pipeline no lo reporte como éxito.

met webhooks sigue disponible temporalmente como alias de met inbound-webhooks y avisa que está deprecado.

v2.25.0

Resuelto es el estado final de los tickets de soporte

La API de partners simplifica el ciclo de soporte: resolved es ahora el único estado terminal y closed deja de ser un valor aceptado.

Los estados válidos de PATCH /partner/tickets/:id/status son:

new · in_progress · waiting_client · resolved

Un ticket resuelto ya no se puede reabrir ni recibir respuestas. Si el problema persiste, crea un ticket nuevo; así cada caso conserva un inicio, una resolución y un historial inequívocos.

v2.25.0

Los contactos gemelos ahora se detectan y se pueden fusionar

Qué cambió

GET /contacts/{id}/duplicates gana un motivo de coincidencia nuevo: identidad.

Hasta hoy la búsqueda de duplicados comparaba teléfono y email. Eso deja fuera el caso más incómodo: dos contactos que son literalmente la misma conversación —mismo user_ns, mismo canal, misma línea—. Pasa cuando alguien te escribe por primera vez mandando varios mensajes seguidos, y también cuando una importación trae el número con + y el chat entrante lo trae sin él.

{
  "data": [
    {
      "id": 33325,
      "name": "Cristhian Martinez",
      "phone": "+573172723452",
      "matched_by": ["identidad", "phone"],
      "can_merge": true,
      "blocked_reason": null
    }
  ]
}

matched_by ahora puede traer "identidad" además de "phone" y "email". Es aditivo: si ya lees ese arreglo, los valores viejos siguen llegando igual.

Lo que antes no se podía fusionar, ahora sí

can_merge era false en cuanto los dos contactos tenían conversación propia, sin mirar si era la *misma* conversación. Los gemelos exactos caían justo ahí y quedaban trabados.

Ahora la regla mira el valor, no su presencia:

  • Misma identidad (mismo user_ns, mismo canal, misma línea) → can_merge: true. Se fusionan y la identidad que sobrevive queda en dígitos, sin +.
  • Identidades distintas con conversación propia cada una → sigue bloqueado, con su blocked_reason. Fusionarlas perdería una y el próximo mensaje desharía la fusión.

Un + de más no hace dos identidades: +573172723452 y 573172723452 son la misma persona. Los identificadores que no son teléfonos (los de Meteor CRM, los BSUID de WhatsApp) se siguen comparando literales.

Qué tienes que hacer

Nada obligatorio. Si guardas la identidad del contacto por tu lado, conviene que la normalices a dígitos: es el formato con el que entra WhatsApp y con el que queda un contacto después de fusionarse.

v2.11.0

Reporta conversiones web (Pixel) con deduplicación

Ahora puedes reportar conversiones web a Meta como espejo server-side de tu Pixel, para no perder eventos que el navegador bloquea (ad-blockers, iOS, cookies).

  • POST /api/v1/conversions/web — scope conversions:write.
  • Pasa el mismo event_id que disparaste en el navegador con fbq('track', …, { eventID }). Meta deduplica Pixel↔CAPI durante 48h con esa clave, así que el evento cuenta una sola vez aunque llegue por los dos caminos.
  • No es por-contacto: la coincidencia va por fbp/fbc (cookies del navegador) o por PII opcional (email, phone, external_id, que hasheamos antes de emitir). Necesitas al menos una clave de match.
  • Reenvía la IP y el User-Agent del usuario final (no los de tu servidor) en client_ip_address / client_user_agent para mejorar la coincidencia.
  • Opcionales: event_source_url, value, currency.
  • Devuelve la decisión real: accepted (encolado), discarded (con razón: integración inactiva, fuera de taxonomía, sin clave de match) o duplicate.

Los eventos web usan los nombres de evento del Pixel (no los de *business messaging* de WhatsApp) — configúralos en la taxonomía del workspace.

SDK: met.conversions.reportWeb({ event_name, event_id, fbp, … }).

v2.10.0

Reporta conversiones a Meta desde la API

Ya puedes reportar conversiones a Meta (Conversions API) por nuestra API pública, con el mismo criterio que aplica el Met: una compuerta valida atribución del anuncio, taxonomía y corroboración antes de emitir. Tú mandas el hecho; nosotros decidimos y emitimos.

  • POST /api/v1/conversions — scope conversions:write.
  • Body: contact_id (el contacto de _contacts), event_name (debe estar en la taxonomía del workspace), y opcionales value, currency, evidence, bucket.
  • Devuelve la decisión real: accepted (encolado para emisión), pending_corroboration (esperando el hecho objetivo), discarded (con la razón: sin atribución, fuera de taxonomía, etc.) o duplicate.

Para WhatsApp, Meta exige nombres de evento de *business messaging* (ej. Purchase, LeadSubmitted, InitiateCheckout) — no los del Pixel web. Este origen (api) puede confirmar Purchase directamente, porque representa un hecho objetivo de tu sistema.

Soporta Idempotency-Key. Es una key de efecto externo real: las keys met_test_ no pueden emitir (evita eventos reales a Meta desde pruebas).

v2.24.0

La recarga automática ahora tiene techo mensual

Qué cambió

La recarga automática de Energía puede tener un tope de gasto por mes. Mientras el tope no se alcance, todo funciona igual; al alcanzarlo dejamos de cobrar la tarjeta y la recarga se reanuda sola el primer día del mes siguiente.

GET /billing/auto-recharge trae tres campos nuevos:

{
  "enabled": true,
  "threshold_usd": 5,
  "amount_usd": 50,
  "monthly_cap_usd": 200,
  "month_spent_usd": 150,
  "capped": false
}
  • monthly_cap_usd — el techo, en USD. null significa sin tope, que es como sigue configurada toda cuenta que no lo haya cambiado.
  • month_spent_usd — cuánto lleva gastado la recarga automática este mes. Una recarga hecha a mano no consume el tope. El mes corta en la zona horaria del workspace y el contador vuelve a cero solo.
  • cappedtrue cuando la siguiente recarga ya no cabe en el tope. Ojo con esto: no es lo mismo que haber gastado el tope entero. Con un tope de 200, 180 gastados y recargas de 50, capped ya es true aunque queden 20 sin usar.

Qué tienes que hacer

Nada, si no lees esta configuración. Los tres campos vienen siempre, y las cuentas sin tope los reportan como null, 0 y false.

Si muestras el estado de la recarga automática, capped merece su propio mensaje. Por dentro se parece a una recarga pausada —no se cobra—, pero no es una falla: es el límite que la propia cuenta pidió. Tratarlo como error manda a revisar una tarjeta que está perfecta.

if (config.capped) {
  // Llegó al tope del mes. Se reanuda sola; no hay nada roto.
} else if (config.status === 'failed') {
  // Esto sí: la tarjeta rechazó el cobro.
}

Por qué te conviene mirarlo ahora

Sin tope, activar la recarga automática es autorizar un cobro sin techo: si algo consume Energía en bucle, la tarjeta se cobra tantas veces como haga falta y la cuenta se entera por el extracto. Es la razón por la que mucha gente la deja apagada — y con ella apagada, los Mets se detienen cuando el saldo se acaba. El tope es lo que permite dejarla encendida sin firmar un cheque en blanco.

v2.23.0

El destinatario de WhatsApp ya no siempre es un teléfono

Qué cambió

WhatsApp está habilitando los usernames: una persona puede elegir un nombre de usuario y conversar contigo sin darte su número. Cuando eso pasa, Meta deja de enviar el teléfono y en su lugar manda un identificador propio, el *business-scoped user ID* (CO.13491208655302741918), que es específico de tu cuenta: el mismo cliente tiene otro identificador distinto en otro negocio.

Por eso el dry-run de plantillas —POST /channels/{channelId}/whatsapp/send-template con dry_run=true— tiene un campo nuevo:

{
  "dry_run": true,
  "would_send": {
    "recipient": "CO.13491208655302741918",
    "phone_number_id": "109876543210987",
    "template": { "name": "recordatorio_cita", "language": "es" }
  }
}

would_send.to sigue siendo el teléfono y llega igual que siempre para los contactos que lo tienen. Cuando el contacto llegó por username, to no viene y en su lugar está recipient. Siempre recibes uno de los dos, nunca los dos a la vez.

Qué tienes que hacer

Si lees would_send.to para mostrarlo o registrarlo, deja de asumir que existe:

const destino = would_send.to ?? would_send.recipient;

Nada más cambia. Los envíos reales siguen funcionando igual y no tienes que pasar el identificador a mano en ninguna llamada: Met resuelve solo a quién le habla, según lo que Meta nos haya dado de ese contacto.

Por qué te conviene mirarlo ahora

Si guardas a tus clientes usando el teléfono como llave, esa llave va a faltar para quien adopte un username — y ahí no vas a poder reconocerlo ni volver a contactarlo. Conviene que tu sistema empiece a guardar también el identificador y que no exija el número para crear un registro.

v2.24.0

Los conteos de bandeja responden por lo que estás mirando

GET /contacts/counts ahora acepta filtros

Hasta hoy el endpoint devolvía siempre los totales del workspace. Si querías saber cuántos chats abiertos tiene una asesora, el conteo no te servía: había que traer la lista y contarla en tu código.

Ahora acepta los mismos parámetros que GET /contactsq, status, assigned_to, agent_group, channel_id, tag, unread, autopilot y filters — y devuelve los conteos facetados.

Facetado quiere decir que cada bloque se calcula con todos los filtros activos menos el suyo:

GET /contacts/counts?assigned_to=42&status=open
  • Los cinco contadores de estado (open, pending, human_requested, finished, archived) cuentan los chats de la asesora 42, ignorando status.
  • Los de asignación (total, mine, unassigned, byUser) cuentan los chats abiertos, ignorando assigned_to.
  • byGroup ignora agent_group; byChannel ignora channel_id.

Un bloque que se aplicara su propio filtro daría cero en todas las demás opciones, y quien lo lee se quedaría sin forma de saber qué hay del otro lado.

Un campo nuevo: scopeTotal

{ "total": 287, "scopeTotal": 164, "open": 5, "...": "..." }

scopeTotal es el universo de la carpeta que elegiste — el asesor, el grupo o el canal — sin estado, búsqueda ni filtro avanzado. Es el denominador honesto de un "viendo N de M": total cambia con cada filtro que agregas, así que no sirve para decir de cuánto estás viendo una parte.

Qué no cambia

Si llamas al endpoint sin parámetros, la respuesta es la de siempre: los totales del workspace. scopeTotal es un campo nuevo que se agrega, no reemplaza a ninguno, y los conteos siguen respetando la visibilidad de quien consulta.

El contrato y la referencia ya están actualizados.

v2.23.0

El pipeline de oportunidades tiene una etapa nueva, Negociación

NEGOCIACION, entre Propuesta y Ganado

El campo stage de una oportunidad acepta un valor más: NEGOCIACION. Va justo después de PROPUESTA y antes de GANADO.

curl -X POST https://api.partners.meteor.com.co/leads \
  -H "Authorization: Bearer $MET_API_KEY" \
  -d '{"company_name":"Acme","stage":"NEGOCIACION"}'

El embudo completo queda así: NUEVOCONTACTADOCALIFICADOPROPUESTANEGOCIACIONGANADO, con PERDIDO al costado y ACTIVADO al final, que sigue siendo terminal y la escribe Meteor al vincular la oportunidad con su cuenta.

Antes, todo lo que pasaba entre mandar la cotización y firmar vivía en PROPUESTA. Con las dos cosas en la misma etapa, el conteo no distinguía la oportunidad que espera trabajo tuyo de la que espera la firma del cliente.

Qué hacer con tu integración

Es un valor nuevo, no un cambio de los que ya usabas: nada de lo que hoy envías deja de funcionar, y ninguna oportunidad cambió de etapa sola. Si tu código tipa stage como una lista cerrada de valores, regenera tu cliente o suma NEGOCIACION a mano antes de leer oportunidades que ya estén ahí.

npm install @meteor.ia/sdk@latest
v2.22.0

La referencia ahora dice qué es cada grupo y lleva a su guía

Cada operación tiene un enlace a la guía que la explica

Hasta hoy el viaje era de ida: las guías enlazaban a la referencia, y la referencia no devolvía a ninguna parte. Si llegabas a POST /items buscando cómo se usa de verdad, no tenías desde dónde saltar.

Ahora las 300 operaciones y los 36 grupos del contrato llevan externalDocs con el enlace a su guía. No es un cambio del portal: está en openapi.public.json, así que también lo ve tu cliente generado y cualquier herramienta que lea el contrato.

Además, los grupos dejaron de ser encabezados mudos. El documento declaraba tags: [] — la referencia mostraba Slugs, Item Content o handoff sin una línea que dijera qué son. Ahora cada uno trae su descripción, y los tres cuyo nombre técnico no se lee bien tienen nombre visible (agent-groupsGrupos de Mets).

El contrato dice contra qué versión estás generando

info.version estaba clavado en 1.0.0 desde el primer día. Ahora lleva la versión del changelog, así que el número que ves en el contrato es el mismo que encabeza la entrada donde se explica qué cambió.

curl -s https://developers.meteor.com.co/public/openapi.public.json | jq .info.version

Guárdalo el día que generes tu cliente: comparar contra el changelog te dice exactamente qué pasó en el medio.

Versionado y deprecación, escritos

Nueva guía: Versionado y deprecación. Dice qué cambios puedes esperar sin aviso (y cómo escribir tu integración para que no te rompan), cuáles nunca ocurren sin un ciclo previo, y qué pasa exactamente cuando un endpoint se va a retirar.

Lo concreto: mientras un endpoint deprecado siga vivo responde normal, pero cada respuesta trae Deprecation: true, un Sunset con la fecha de retiro y un Link con la alternativa. Si registras los headers de tus llamadas salientes, esa es la forma más barata de no enterarte tarde. Hoy no hay ningún endpoint deprecado.